G-gen の杉村です。BigQuery は通常の RDBMS と異なり分析用データベースであることから、非正規化したテーブルを扱うことが多くなります。そのための独特のデータ型として、ARRAY (配列) と STRUCT (構造体) があります。これらについて解説します。
概要
BigQuery は Google Cloud (旧称 GCP) の提供するサーバーレスな分析用データベースのサービスです。列志向 (カラムナ) の特徴を持ち、料金プランの選択にもよりますが、デフォルトではスキャンしたデータ量に応じた従量課金です。
クラウド上の分析用データベースのベストプラクティスとして、テーブルを非正規化することでデータ読み取りや結合のコストを下げ、パフォーマンス向上を図ることがあります。今回ご紹介する ARRAY (配列) と STRUCT (構造体) は、いずれもデータを非正規化して持つ型です。これらのデータ型には、Google Analytics 4 や Google Analytics for Firebase、Google Cloud の Billing Export などでデータを BigQuery にエクスポートするとよく出会います。
その独特のクエリの仕方に苦しめられた人も多いのではないでしょうか。当記事ではこれらについて解説し、また組み合わせて使う ARRAY<STRUCT> についてもご紹介します。
また、最後には Google Analytics 4 のデータを模したテーブルに対するクエリの方法も例示します。
ARRAY (配列)
ARRAY とは
ARRAY (配列) とは「ゼロ個以上の同じデータ型の値で構成された順序付きリスト」のことです。以下のようなものを指します。
# INT64 の配列 [2, 3, 5, 7, 11, 13, 17, 19] # STRING の配列 ["国語", "算数", "理科", "社会"]
配列の型を持つテーブルは以下のようなものになります。
(行番号) | name | subjects |
---|---|---|
1 | 田中 太郎 | 国語 |
算数 | ||
理科 | ||
社会 | ||
2 | 鈴木 二郎 | 国語 |
算数 |
※ (行番号) 列は一行のまとまりを示すために便宜的に割り振ったものであり、テーブル内のデータではありません。
上記のテーブルの subjects 列は ARRAY であり、1行に2つ以上の値が入っています。
これはリレーションの第1正規形に違反しており、通常のリレーショナル・データベースではこのようなデータの持ち方をしません。しかし BigQuery のような分析用データベースではこのような配列型を使いリレーションを維持したまま、マスタとトランザクションを分けない大福帳的なテーブル構成にすることで、JOIN のコストが下がりパフォーマンス向上を図ることができます。
- 参考 : Array type
- 参考 : 配列リテラル
- 参考 : 配列の操作
サンプルテーブル
ここからは実際の SQL を紹介しながらイメージを掴んでいきます。以下の WITH 句を仮想的なサンプルテーブルとして使います。
name 列が通常の STRING 型で、subjects 列は STRING 型の値を要素に持つ配列型です。
WITH t0 AS ( SELECT "田中 太郎" AS name, ["国語", "算数", "理科", "社会"] AS subjects UNION ALL SELECT "鈴木 二郎" AS name, ["国語", "算数"] AS subjects ) SELECT * FROM t0;
(行番号) | name | subjects |
---|---|---|
1 | 田中 太郎 | 国語 |
算数 | ||
理科 | ||
社会 | ||
2 | 鈴木 二郎 | 国語 |
算数 |
SELECT
まず SELECT 文の例を見ることでイメージを掴みます (以降、WITH 句は省略します。先ほどの SQL 文後半の SELECT 以降を書き換えてお試しください)。
SELECT name, subjects FROM t0;
上記のように、SELECT 文は通常の列をクエリするときと何ら代わりありません。出力結果は以下のようになり、配列内の全ての値が返ります。
(行番号) | name | subjects |
---|---|---|
1 | 田中 太郎 | 国語 |
算数 | ||
理科 | ||
社会 | ||
2 | 鈴木 二郎 | 国語 |
算数 |
SELECT 〜 WHERE
WHERE 句でフィルタをしようと考えたとき、最初の壁に当たります。
SELECT name, subjects FROM t0 WHERE subjects = "理科";
この SQL は、以下のようなエラーになります。
No matching signature for operator = for argument types: ARRAY<STRING>, STRING. Supported signature: ANY = ANY at [17:3]
=
の左辺と右辺で異なる型 (左辺は ARRAY である subjects 列、右辺は STRING) を指定していることからエラーになっています。なお右辺を ["国語", "算数"]
のように配列にしてもエラーになります (Equality is not defined for arguments of type ARRAY<STRING> at [7:5]
)。=
演算子はそもそも配列型をサポートしていません。
一方で、以下のクエリは成功します。
SELECT name, subjects FROM t0 WHERE "理科" IN UNNEST(subjects);
(行番号) | name | subjects |
---|---|---|
1 | 田中 太郎 | 国語 |
算数 | ||
理科 | ||
社会 |
ここでは UNNEST 関数を用いています。UNNEST() は配列内の全要素を各行に割り振った「テーブル」を返します。以下の例を見てください。
SELECT * FROM UNNEST([1, 2, 3]);
(行番号) | f0_ |
---|---|
1 | 1 |
2 | 2 |
3 | 3 |
このように配列が UNNEST() されると、各要素が1行1行に割り振られた「テーブル形式」になることが分かります。先程の WHERE 句では UNNEST() で subjects 列の中身を分解してテーブル化し、その結果の中に "理科" が含まれているかを IN 演算子で検査して、TRUE となった行だけを返したのです。
- 参考 : 配列内の要素をテーブル内の行に変換する
SELECT 〜 CROSS JOIN
配列型を含むテーブルに自在にフィルタを行うには、UNNEST() と CROSS JOIN を使いこなす必要があります。
一例を示します。
SELECT name, unnested_subject FROM t0 CROSS JOIN UNNEST(subjects) AS unnested_subject;
(行番号) | name | unnested_subject |
---|---|---|
1 | 田中 太郎 | 国語 |
2 | 田中 太郎 | 算数 |
3 | 田中 太郎 | 理科 |
4 | 田中 太郎 | 社会 |
5 | 鈴木 二郎 | 国語 |
6 | 鈴木 二郎 | 算数 |
上記のクエリでは元テーブルの全行に対して1行ずつ「subjects を UNNEST して返ってきたテーブル」を CROSS JOIN しています。これにより、配列が分解されて通常の第1正規化されたテーブルのようになり、WHERE でフィルタできます。
SELECT (SELECT ~ UNNEST)
以下のようなクエリも用いることができます。
SELECT name, (SELECT * FROM UNNEST(subjects) AS u WHERE u = "理科") AS subject_science FROM t0;
(行番号) | name | subject_science |
---|---|---|
1 | 田中 太郎 | 理科 |
2 | 鈴木 二郎 | null |
SELECT 句内のサブクエリの SELECT は、UNNEST() した subjects 列を対象としており、その結果は WHERE で "理科" だけに絞られています。
subjects 列が "理科" の値を持っていない行は null として返ります。
ただしこの例は、(SELECT * FROM UNNEST(subjects) AS u WHERE u = "理科")
の結果が単一行だけ返る (スカラ値である) ことが前提です。もし配列に "理科" が複数ある場合、Scalar subquery produced more than one element
エラーが生じてしまいます。
例えば以下のように、データが重複してしまっているテーブルの場合です。
(行番号) | name | subjects |
---|---|---|
1 | 田中 太郎 | 国語 |
算数 | ||
理科 | ||
社会 | ||
2 | 鈴木 二郎 | 国語 |
算数 | ||
3 | 三中 三郎 | 国語 |
理科 | ||
理科 |
三中 三郎の subjects 列に理科が2行あるため、UNNEST に対する SELECT 結果が2行になってしまい、クエリが失敗します。
このようなデータの不整合がある可能性に対応するには、以下のようにします。
SELECT name, (SELECT DISTINCT * FROM UNNEST(subjects) AS u WHERE u = "理科") AS subject_science FROM t0;
(行番号) | name | subjects |
---|---|---|
1 | 田中 太郎 | 理科 |
2 | 鈴木 二郎 | null |
3 | 三中 三郎 | 理科 |
DISTINCT することで返却結果が一意に絞られ、エラーになりません。
CREATE TABLE / INSERT
順序としてはあべこべですが、ARRAY を持つ表の CREATE TABLE および INSERT 文も紹介します。
CREATE TABLE はシンプルです。ARRAY の列を ARRAY<(型名)> として定義するのみです。
CREATE TABLE `my-project.my_dataset.t0` ( name STRING, subjects ARRAY<STRING> );
INSERT 文は以下です。
INSERT `my-project.my_dataset.t0` VALUES ("鈴木 二郎", ["国語", "算数"]);
上記の ["国語", "算数"]
を ARRAY["国語", "算数"]
や ARRAY<STRING>["国語", "算数"]
のように表現することもできます (意味は変わりません)。
制限
配列の配列を作ることはできません。つまり、以下のようにネストした配列は作れません。
SELECT ["hogehoge", ["foo", "bar"]];
Cannot construct array with element type ARRAY<STRING> because nested arrays are not supported at [2:16]
一方で配列の中に、後に紹介する STRUCT 型は入れることができます。これが Google Analytics 4 の BigQuery Export 等で見られる独特な構成です。これについては後に解説します。
SELECT [ STRUCT("hoge01" AS f1, "fuga01" AS f2), STRUCT("hoge02", "fuga02") ] AS arrayed_struct;
(行番号) | arrayed_struct.f1 | arrayed_struct.f2 |
---|---|---|
1 | hoge01 | fuga01 |
hoge02 | fuga02 |
STRUCT (構造体)
STRUCT とは
BigQuery の STRUCT (構造体) 型は、構造を持った型で、一つ以上のフィールドを持つことができます。
例えば以下の表では、item という列が STRUCT 型で、id という INT64 型のフィールドと、name という STRING 型のフィールドを持っています。
price | item.id | item.name |
---|---|---|
3000 | 10001 | T-shirt |
8000 | 20001 | jacket |
STRUCT はデータを構造的に整理するために有用です。
- 参考 : Struct type
- 参考 : 構造体リテラル
サンプルテーブル
STRUCT 型を持つテーブルの例として、以下の WITH 句を仮想的なサンプルテーブルとして使います。
school 列が STRUCT 型で、フィールドとして year (INT64 型) と class (STRING 型) を持っています。
WITH t1 AS ( SELECT "田中 太郎" AS name, STRUCT(1 AS year, "B" AS class) AS school UNION ALL SELECT "鈴木 二郎" AS name, STRUCT(2 AS year, "C" AS class) AS school ) SELECT * FROM t1;
(行番号) | name | school.year | school.class |
---|---|---|---|
1 | 田中 太郎 | 1 | B |
2 | 鈴木 二郎 | 2 | C |
SELECT
STRUCT 型に対する SELECT は、他の通常の型と大きく変わらないため、直感的に理解できます。
このようなテーブルに対するクエリは以下のようになります。
SELECT name, school FROM t1;
(行番号) | name | school.year | school.class |
---|---|---|---|
1 | 田中 太郎 | 1 | B |
2 | 鈴木 二郎 | 2 | C |
school という列名を指定するだけで、配下のフィールドの全てが選択されます。
以下のように、フィールド名を明示して SELECT することも可能です。
SELECT name, school.year FROM t1;
(行番号) | name | year |
---|---|---|
1 | 田中 太郎 | 1 |
2 | 鈴木 二郎 | 2 |
なおこの場合、返却結果の列名はフィールド名だけになります。
SELECT 〜 WHERE
ARRAY と異なり、STRUCT 型への WHERE 句の使用は直感的です。
SELECT name, school FROM t1 WHERE school.year = 1;
(行番号) | name | school.year | school.class |
---|---|---|---|
1 | 田中 太郎 | 1 | B |
WHERE 句でフィールドに対して条件を指定するだけで、フィルタをすることができます。
CREATE TABLE / INSERT
STRUCT 型を持つテーブルの CREATE TABLE 文は、以下のように記述します。STRUCT<(フィールド名) (型名), (フィールド名) (型名), ...> のように各フィールドの名前と型を指定します。
CREATE TABLE `my-project.my_dataset.t1` ( name STRING, school STRUCT<year INT64, class STRING> );
INSERT 文は以下です。
INSERT INTO `my-project.my_dataset.t1` VALUES ("鈴木 二郎", STRUCT(2 AS year, "C" AS class));
なお AS year
AS class
のようなフィールド名指定は省略可能で、実は可読性を高める以外の意味はありません。フィールド名を指定したところで記載順番が優先されます。すなわち上記を "C" AS class, 2 AS year
と書き換えると、型エラーになります。逆に、型が同じだと意図しない列に値が入ってしまいます。
以下のように簡易的に記述することもできます。
INSERT INTO `my-project.my_dataset.t1` VALUES ("鈴木 二郎", (2 , "C"));
制限
STRUCT 型はネストすることができますが、最大で15段階までです。
ネストした STRUCT 型の例は以下です。
SELECT STRUCT( 1001 AS f_int, "hoge" AS f_string, STRUCT( "foo" AS f1, "bar" AS f2 ) AS f_struct ) AS root;
(行番号) | root.f_int | root.f_string | root.f_struct.f1 | root.f_struct.f2 |
---|---|---|---|---|
1 | 1001 | hoge | foo | bar |
ARRAY<STRUCT> (ネストされた繰り返し列)
ARRAY<STRUCT> とは
ARRAY<STRUCT> (ネストされた繰り返し列) とはここまで紹介した ARRAY と STRUCT を組み合わせたものです。
ARRAY の列の中に、要素として STRUCT 型が入っているものです。Google Analytics 4 (Firebase) や Google Cloud の Billing Export でこの形式が見られます。
実例としては、以下のようなものです。
(行番号) | name | classes.subject | classes.teacher |
---|---|---|---|
1 | 田中 太郎 | 国語 | 斉藤 三郎 |
英語 | 伊東 四朗 | ||
2 | 鈴木 二郎 | 英語 | 伊東 四朗 |
classes 列は ARRAY (配列) 型ですが、その各要素は subject と teacher というフィールドを持つ STRUCT 型です。
サンプルテーブル
サンプルテーブルとして、以下の WITH 句を使って試してください。
classes 列が「ネストされた繰り返し列」です。配列であり、各要素は subject という STRING 型フィールドと teacher という STRING 型フィールドを持った STRUCT です。
WITH t2 AS ( SELECT "田中 太郎" AS name, [ STRUCT("国語" AS subject, "斉藤 三郎" AS teacher), STRUCT("英語" AS subject, "伊東 四朗" AS teacher) ] AS classes UNION ALL SELECT "鈴木 二郎" AS name, [ STRUCT("英語" AS subject, "伊東 四朗" AS teacher) ] AS classes ) SELECT * FROM t2;
(行番号) | name | classes.subject | classes.teacher |
---|---|---|---|
1 | 田中 太郎 | 国語 | 斉藤 三郎 |
英語 | 伊東 四朗 | ||
2 | 鈴木 二郎 | 英語 | 伊東 四朗 |
SELECT
classes 列を指定して SELECT することは可能です。
SELECT name, classes FROM t2;
しかし、以下のように classes 列のフィールドを指定してクエリしようとすると、エラーになります。
SELECT name, classes.subject FROM t2;
Cannot access field subject on a value with type ARRAY<STRUCT<subject STRING, teacher STRING>> at [18:11]
classes は ARRAY です。classes 列の subject だけを取り出したい場合、以下のように UNNEST() してテーブル化し、そこから subject 列だけを取り出すことができます。ARRAY を先頭につけないと、単体の値 (スカラ値) が返ってくることが期待され Scalar subquery produced more than one element
になってしまうため、ARRAY() として戻りが配列であることを明示します。
SELECT name, ARRAY(SELECT subject FROM UNNEST(classes)) AS subject FROM t2;
(行番号) | name | subject |
---|---|---|
1 | 田中 太郎 | 国語 |
英語 | ||
2 | 鈴木 二郎 | 英語 |
SELECT 〜 WHERE (エラー)
WHERE を使ったフィルタも独特です。以下のクエリはエラーになります。
SELECT name, classes FROM t2 WHERE classes.subject = "国語";
Cannot access field subject on a value with type ARRAY<STRUCT<subject STRING, teacher STRING>> at [22:11]
先程と同じ理由で、配列である classes の一要素の subject フィールドにはアクセスできません。
ARRAY<STRUCT>
をフィルタするには、次に示すようなクエリを用います。
SELECT 〜 CROSS JOIN
以下のクエリは通ります。
SELECT name, c.subject, c.teacher FROM t2 CROSS JOIN UNNEST(classes) AS c WHERE c.subject = "国語";
(行番号) | name | subject | teacher |
---|---|---|---|
1 | 田中 太郎 | 国語 | 斉藤 三郎 |
ARRAY である classes 列を毎行 unnest して CROSS JOIN し、第1正規化された状態のテーブルに WHERE でフィルタをかけています。
SELECT (SELECT ~ UNNEST)
以下ようなクエリもできます。
SELECT name, ( SELECT AS STRUCT subject, teacher FROM UNNEST(classes) WHERE subject = "国語" ) AS class FROM t2;
(行番号) | name | class.subject | class.teacher |
---|---|---|---|
1 | 田中 太郎 | 国語 | 斉藤 三郎 |
2 | 鈴木 二郎 | null | null |
こちらもやっていることは似ており、SELECT 句の中のサブクエリで毎行 classes を UNNEST して subject と teacher を取り出し、その結果を WHERE 句で絞っています。
SELECT で指定する列は単一の値 (スカラ値) を期待するので SELECT AS STRUCT
を使うことで、戻りが STRUCT であることを明示的に指示しています。AS STRUCT
をつけないと以下のようなエラーになります。
Scalar subquery cannot have more than one column unless using SELECT AS STRUCT to build STRUCT values at [18:3]
もしくは AS STRUCT
をつけなくても、SELECT する列が subject か teacher の一つだけであればエラーになりません。
CREATE TABLE / INSERT
ARRAY<STRUCT>
の CREATE TABLE 文は以下のようになります。
CREATE TABLE `my-project.my_dataset.t2` ( name STRING, classes ARRAY< STRUCT< subject STRING, teacher STRING > > );
INSERT 文は以下です。
INSERT `my-project.my_dataset.t2` VALUES( "田中 太郎", [ STRUCT( "国語" AS subject, "斉藤 三郎" AS teacher ), STRUCT( "英語" AS subject, "伊東 四朗" AS teacher ) ] );
なお通常の STRUCT 型と同じで AS subject
AS teacher
のようなフィールド名の指定は省略可能で、むしろスキーマ定義と同じ順番で記載する必要があります。
実践 : Google Analytics 4 データへのクエリ
概要
Google Analytics 4 (GA4) や Firebase ではアクセス情報を BigQuery にエクスポートすることができます。簡単な設定で BigQuery に定期的にデータを吐き出してくれるので便利ですが、そのスキーマには当記事で紹介した ARRAY<STRUCT>
が使われており、クエリの仕方が独特です。
当項目では、GA4 のエクスポートデータのテーブルを模したテーブルに対するクエリの方法を簡単にご紹介します。
テーブル例
以下のような模擬テーブルを用います。
フィールド名 | 種類 | モード |
---|---|---|
event_timestamp | INTEGER | NULLABLE |
event_name | STRING | NULLABLE |
event_params | RECORD (=STRUCT) | REPEATED (=ARRAY) |
├ key | STRING | NULLABLE |
└ value | RECORD (=STRUCT) | NULLABLE |
├ string_value | STRING | NULLABLE |
├ int_value | INTEGER | NULLABLE |
├ float_value | FLOAT | NULLABLE |
└ double_value | FLOAT | NULLABLE |
データは以下のように入っています。
クエリ例1: 特定ページの PV 数を集計
特定ページの PV 数を計測するため event_name
が "page_view"
で、かつ page_location
が "https://blog.g-gen.co.jp/entry/deploy-preview-using-cloud-run-tagged-revision"
という文字列となっているレコードを選択したいとします。
以下のようにしたいところですが、エラーになります。
SELECT COUNT(*) AS pv FROM `my-project.my_dataset.ga4_mock` WHERE event_name = "page_view" AND event_params.value.string_value = "https://blog.g-gen.co.jp/entry/deploy-preview-using-cloud-run-tagged-revision";
Cannot access field value on a value with type ARRAY<STRUCT<key STRING, value STRUCT<string_value STRING, int_value INT64, float_value FLOAT64, ...>>> at [8:16]
フィルタ対象の event_params
は ARRAY であり、その要素である event_params.value
は STRUCT 型でありそのフィールドの一つが string_value
です。どのようにクエリすれば良いのか、頭がこんがらがってしまいます。
以下のようなクエリが通ります。
SELECT COUNT(*) AS pv FROM `my-project.my_dataset.ga4_mock` WHERE event_name = "page_view" AND "https://blog.g-gen.co.jp/entry/deploy-preview-using-cloud-run-tagged-revision" IN (SELECT value.string_value FROM UNNEST (event_params) WHERE key = "page_location")
(行番号) | pv |
---|---|
1 | 1 |
このクエリでは、event_name 列が "page_view" の行に対し、配列である event_param 列を UNNEST して平準化テーブルとし、その中で key が "page_location" である行の value.string_value フィールドを IN で検査しています。
また別案として、以下のようなビューを作ってしまうのも手です。
SELECT event_timestamp, event_name, ( SELECT value.string_value FROM UNNEST(event_params) WHERE key = "page_location" ) AS page_location FROM `my-project.my_dataset.ga4_mock` WHERE event_name = "page_view";
(行番号) | event_timestamp | event_name | page_location |
---|---|---|---|
1 | 1695781266341693 | page_view | https://blog.g-gen.co.jp/entry/deploy-preview-using-cloud-run-tagged-revision |
このように page_view イベントの page_location 値のビューだけを作ってしまえば、あとは一般的な SQL でクエリできます。CREATE VIEW AS
でビューを作るか、一時的・探索的なクエリなら WITH 句を使っても良いでしょう。
WITH unnested_view AS ( SELECT event_timestamp, event_name, ( SELECT value.string_value FROM UNNEST(event_params) WHERE key = "page_location" ) AS page_location FROM `my-project.my_dataset.ga4_mock` WHERE event_name = "page_view" ) SELECT COUNT(*) AS pv FROM unnested_view WHERE page_location = "https://blog.g-gen.co.jp/entry/deploy-preview-using-cloud-run-tagged-revision";
(行番号) | pv |
---|---|
1 | 1 |
上記のように UNNEST して平坦化した状態をデータマートやビューとして持っておけば、BI ツールや Connected Sheets からも利用しやすくなります。
このように ARRAY
型や ARRAY<STRUCT>
型は、SELECT 句または WHERE 句のサブクエリで UNNEST() 関数を使うことで、自在に扱うことができます。
クエリ例2 : string_value を取り出す関数を作る
前述の例だとサブクエリが長く、可読性に劣ります。UNNEST() する処理を関数に切り出して可読性を高めてみます。
CREATE TEMP FUNCTION getStringValue(col ANY TYPE, col_key STRING) AS ( (SELECT c.value.string_value FROM UNNEST(col) c WHERE c.key = col_key) ); SELECT event_timestamp, event_name, getStringValue(event_params, "page_title") AS page_title, getStringValue(event_params, "page_location") AS page_location FROM `my-project.my_dataset.ga4_mock`
結果は以下のようになります。
(行番号) | event_timestamp | event_name | page_title | page_location |
---|---|---|---|---|
1 | 1695781266341693 | page_view | プルリクエストをトリガとするCloud Runのプレビュー環境自動デプロイを実装してみた - G-gen Tech Blog | https://blog.g-gen.co.jp/entry/deploy-preview-using-cloud-run-tagged-revision |
2 | 1695781266341693 | first_visit | プルリクエストをトリガとするCloud Runのプレビュー環境自動デプロイを実装してみた - G-gen Tech Blog | https://blog.g-gen.co.jp/entry/deploy-preview-using-cloud-run-tagged-revision |
スキーマ表記
概要
BigQuery でのスキーマ表記は「SQL 上の表記」と「Web コンソールや JSON 形式での表記」が異なる場合があります。
例えば前述の ARRAY<STRUCT>
は「タイプが RECORD でモードが REPEATED」のように表現されます。
- 参考 : スキーマの指定
以下に参考情報として記載します。
ARRAY
BigQuery コンソール上や JSON 形式でのスキーマ表現では、ここまで扱ったような ARRAY
は以下のように「種類が (配列内のデータの型名) でモードが REPEATED」として表現されます。
{ 〜略〜 "schema": { "fields": [ { "name": "name", "type": "STRING" }, { "mode": "REPEATED", "name": "subjects", "type": "STRING" } ] }, 〜略〜 }
STRUCT
BigQuery コンソール上や JSON 形式でのスキーマ表現では、STRUCT は以下のように「種類が RECORD でモードが NULLABLE/REQUIRED」として表現されます。
{ 〜略〜 "schema": { "fields": [ { "name": "name", "type": "STRING" }, { "fields": [ { "name": "year", "type": "INTEGER" }, { "name": "class", "type": "STRING" } ], "name": "school", "type": "RECORD" } ] }, 〜略〜 }
ARRAY<STRUCT>
BigQuery コンソール上や JSON 形式でのスキーマ表現では、ARRAY<STRUCT>
は以下のように「種類が RECORD でモードが REPEATED」として表現されます。RECORD は STRUCT と、REPEATED は ARRAY と同義です。
JSON では以下のようになります。
{ 〜略〜 "schema": { "fields": [ { "name": "name", "type": "STRING" }, { "fields": [ { "name": "subject", "type": "STRING" }, { "name": "teacher", "type": "STRING" } ], "mode": "REPEATED", "name": "classes", "type": "RECORD" } ] }, 〜略〜 }
杉村 勇馬 (記事一覧)
執行役員 CTO / クラウドソリューション部 部長
元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 12資格、Google Cloud認定資格11資格。X (旧 Twitter) では Google Cloud や AWS のアップデート情報をつぶやいています。
Follow @y_sugi_it