Verschachtelte Daten in BigQuery (wiederholte Datensätze)

BigQuery unterstützt verschachtelte Datensätze in Tabellen. Verschachtelte Datensätze können ein einzelner Datensatz sein oder wiederkehrende Werte enthalten. Diese Seite bietet einen Überblick über die Arbeit mit verschachtelten BigQuery-Daten in Looker.

Vorteile verschachtelter Datensätze

Die Verwendung verschachtelter Datensätze beim Scannen eines verteilten Datasets bietet einige Vorteile:

  • Für verschachtelte Datensätze sind keine Joins erforderlich. Das bedeutet, dass Berechnungen schneller sein können und viel weniger Daten scannen, als wenn Sie die zusätzlichen Daten bei jeder Abfrage neu verknüpfen müssten.
  • Verschachtelte Strukturen sind im Wesentlichen vorab verknüpfte Tabellen. Wenn Sie nicht auf die verschachtelte Spalte verweisen, entstehen keine zusätzlichen Kosten für die Abfrage, da BigQuery-Daten in Spalten gespeichert werden. Wenn Sie auf die verschachtelte Spalte verweisen, entspricht die Logik einem zusammenhängenden Join.
  • Durch verschachtelte Strukturen werden wiederholte Daten vermieden, die in einer breiten, denormalisierten Tabelle wiederholt werden müssten. Mit anderen Worten, für eine Person, die in fünf Städten gelebt hat, würde eine breite denormalisierte Tabelle alle ihre Informationen in fünf Zeilen enthalten (eine für jede der Städte, in denen sie gelebt hat). In einer verschachtelten Struktur nehmen die wiederholten Informationen nur eine Zeile ein, da das Array mit fünf Städten in einer einzigen Zeile enthalten und bei Bedarf entschachtelt werden kann.

Mit verschachtelten Datensätzen in LookML arbeiten

Die folgende BigQuery-Tabelle persons_living zeigt ein typisches Schema mit Beispielnutzerdaten, darunter fullName, age, phoneNumber und citiesLived sowie den Datentyp und den Modus jeder Spalte. Das Schema zeigt, dass sich die Werte in der Spalte citiesLived wiederholen. Dies deutet darauf hin, dass einige Nutzer möglicherweise in mehreren Städten gelebt haben:

Das folgende Beispiel ist die LookML für die Explores und Ansichten, die Sie anhand des vorherigen Schemas erstellen können. Es gibt drei Ansichten: persons, persons_cities_lived und persons_phone_number. Der Explore sieht identisch aus wie ein Explore, das mit nicht verschachtelten Tabellen geschrieben wird.

Hinweis: Im folgenden Beispiel sind alle Komponenten (Ansichten und Explore) in einem Codeblock geschrieben. Es empfiehlt sich jedoch, Ansichten in einzelnen Ansichtsdateien zu platzieren und Explores und die connection:-Spezifikation in der Modelldatei zu platzieren.

-- model file

connection: "bigquery_publicdata_standard_sql"

explore: persons {

  # Repeated nested object
  join: persons_cities_lived {
    view_label: "Persons: Cities Lived:"
    sql: LEFT JOIN UNNEST(persons.citiesLived) as persons_cities_lived ;;
    relationship: one_to_many
  }

  # Non repeated nested object
  join: persons_phone_number {
    view_label: "Persons: Phone:"
    sql: LEFT JOIN UNNEST([${persons.phoneNumber}]) as persons_phone_number ;;
    relationship: one_to_one
  }

}

-- view files

view: persons {

 sql_table_name: bigquery-samples.nested.persons_living ;;

  dimension: id {
    primary_key: yes
    sql: ${TABLE}.fullName ;;
  }

  dimension: fullName {label: "Full Name"}

  dimension: kind {}

  dimension: age {type:number}

  dimension: citiesLived {hidden:yes}

  dimension: phoneNumber {hidden:yes}

  measure: average_age {
    type: average
    sql: ${age} ;;
    drill_fields: [fullName,age]
  }

  measure: count {
    type: count
    drill_fields: [fullName, cities_lived.place_count, age]
  }
}

view: persons_phone_number {

  dimension: areaCode {label: "Area Code"}

  dimension: number {}
}

view: persons_cities_lived {

  dimension: id {
    primary_key: yes
    sql: CONCAT(CAST(${persons.fullName} AS STRING),'|', CAST(${place} AS STRING)) ;;
  }

  dimension: place {}

  dimension: numberOfYears {
    label: "Number Of Years"
    type: number
  }

  measure: place_count {
    type: count
    drill_fields: [place, persons.count]
  }

  measure: total_years {
    type: sum
    sql: ${numberOfYears} ;;
    drill_fields: [persons.fullName, persons.age, place, numberOfYears]
  }

}

Die einzelnen Komponenten für die Arbeit mit verschachtelten Daten in LookML werden in den folgenden Abschnitten ausführlicher erläutert:

Aufrufe

Jeder verschachtelte Datensatz wird als Ansicht geschrieben. In der Ansicht phoneNumber werden beispielsweise einfach die Dimensionen deklariert, die im Datensatz enthalten sind:

view: persons_phone_number {

  dimension: areaCode {label: "Area Code"}

  dimension: number {}

}

Die Ansicht persons_cities_lived ist komplexer. Wie im LookML-Beispiel gezeigt, definieren Sie die Dimensionen, die im Datensatz enthalten sind (numberOfYears und place). Sie können jedoch auch einige Messwerte definieren. Die Kennzahlen und drill_fields werden wie gewohnt definiert, als ob diese Daten in einer eigenen Tabelle wären. Der einzige echte Unterschied besteht darin, dass Sie id als primary_key deklarieren, damit die Summen richtig berechnet werden.

view: persons_cities_lived {

  dimension: id {
    primary_key: yes
    sql: CONCAT(CAST(${persons.fullName} AS STRING),'|', CAST(${place} AS STRING)) ;;
  }

  dimension: place {}

  dimension: numberOfYears {
    label: "Number Of Years"
    type: number
  }

  measure: place_count {
    type: count
    drill_fields: [place, persons.count]
  }

  measure: total_years {
    type: sum
    sql: ${numberOfYears} ;;
    drill_fields: [persons.fullName, persons.age, place, numberOfYears]
  }

}

Erklärungen aufzeichnen

In der Ansicht, die die Teildatensätze enthält (in diesem Fall persons), müssen Sie die Datensätze deklarieren. Sie werden beim Erstellen der Joins verwendet. Sie können diese LookML-Felder mit dem Parameter hidden ausblenden, da Sie sie bei der Untersuchung der Daten nicht benötigen.

view: persons {

  ...
  dimension: citiesLived {
    hidden:yes
    }

  dimension: phoneNumber {
    hidden:yes
    }
  ...

}

Joins

Verschachtelte Einträge in BigQuery sind Arrays von STRUCT-Elementen. Die Join-Beziehung wird nicht über einen sql_on-Parameter, sondern in die Tabelle eingebunden. In diesem Fall können Sie den Join-Parameter sql: für den UNNEST-Operator verwenden. Abgesehen von dieser Abweichung entspricht das Aufheben des Verschachtelungsgrades eines Arrays von STRUCT-Elementen genau dem Zusammenführen einer Tabelle.

Bei nicht wiederholten Einträgen können Sie einfach STRUCT verwenden. Wenn Sie daraus ein Array mit STRUCT-Elementen erstellen möchten, setzen Sie es in eckige Klammern. Das mag zwar seltsam erscheinen, aber es scheint keine Leistungseinbußen zu geben. So bleiben die Dinge übersichtlich und einfach.

explore: persons {

  # Repeated nested object
  join: persons_cities_lived {
    view_label: "Persons: Cities Lived:"
    sql: LEFT JOIN UNNEST(persons.citiesLived) as persons_cities_lived ;;
    relationship: one_to_many
  }

  # Non repeated nested object
  join: persons_phone_number {
    view_label: "Persons: Phone:"
    sql: LEFT JOIN UNNEST([${persons.phoneNumber}]) as persons_phone_number ;;
    relationship: one_to_one
  }

}

Zusammenführen von Arrays ohne eindeutige Schlüssel für jede Zeile

Es ist zwar am besten, eindeutige natürliche Schlüssel in den Daten oder im ETL-Prozess erstellte Ersatzschlüssel zu haben, dies ist jedoch nicht immer möglich. Es kann beispielsweise vorkommen, dass einige Arrays keinen relativ eindeutigen Schlüssel für die Zeile haben. Hier kann WITH OFFSET in der Join-Syntax hilfreich sein.

Beispielsweise kann eine Spalte, die eine Person darstellt, mehrmals geladen werden, wenn die Person in mehreren Städten gelebt hat (z. B. Chicago, Denver oder San Francisco). Es kann schwierig sein, einen Primärschlüssel für die nicht verschachtelte Zeile zu erstellen, wenn kein Datum oder ein anderer identifizierbarer natürlicher Schlüssel angegeben wurde, um die Dauer der Zugehörigkeit der Person in den einzelnen Städten zu unterscheiden. Hier kann WITH OFFSET eine relative Zeilennummer (0,1,2,3) für jede nicht verschachtelte Zeile angeben. Dieser Ansatz garantiert einen eindeutigen Schlüssel für die nicht verschachtelte Zeile:

explore: persons {

  # Repeated nested Object
  join: persons_cities_lived {
    view_label: "Persons: Cities Lived:"
    sql: LEFT JOIN UNNEST(persons.citiesLived) as persons_cities_lived WITH OFFSET as person_cities_lived_offset;;
    relationship: one_to_many
  }

}

view: persons_cities_lived {

  dimension: id {
    primary_key: yes
    sql: CONCAT(CAST(${persons.fullName} AS STRING),'|', CAST(${offset} AS STRING)) ;;
  }

  dimension: offset {
    type: number
    sql: person_cities_lived_offset;;
  }

}

Einfache wiederholte Werte

Verschachtelte Daten in BigQuery können auch einfache Werte wie Ganzzahlen oder Strings sein. Um die Verschachtelung von Arrays mit einfachen wiederholten Werten aufzulösen, können Sie einen ähnlichen Ansatz wie zuvor verwenden und den UNNEST-Operator in einem Join verwenden.

Im folgenden Beispiel wird ein bestimmtes Array von Ganzzahlen, „unresolved_skus“, entschachtelt:

explore: impressions {
  join: impressions_unresolved_sku {
    sql: LEFT JOIN UNNEST(unresolved_skus) AS impressions_unresolved_sku ;;
    relationship: one_to_many
  }

}

view: impressions_unresolved_sku {

  dimension: sku {
    type: string
    sql: ${TABLE} ;;
  }

}

Der Parameter sql für das Array von Ganzzahlen, unresolved_skus, wird als ${TABLE} dargestellt. Damit wird direkt auf die Wertetabelle verwiesen, die dann in explore entschachtelt wird.