[[BigQuery]] で学習用・検証用のダミーデータ (本番データではなく SQL 内に固定値で書き下すもの) を CTE などに埋め込む方法には、`UNION ALL` / `UNNEST` + inline `STRUCT` / `UNNEST` + 型付き `ARRAY<STRUCT<>>` の 3 記法がある。 型明示度と DRY 性で優位なのは最後の **typed UNNEST** で、以下のいずれかに該当する場面では第一選択になる。 - 行数が 10 件超 (→ 3. 可読性) - NULL 混入 (→ 1. 型安定性) - 列の増減が予想される (→ 2. 保守性) ## 3 記法の例 同じ 3 行データを 3 通りで表現する。 ### A. UNION ALL ```sql with data as ( select 1 as id, 'a' as label union all select 2, 'b' union all select 3, null ) select * from data; ``` ### B. UNNEST + inline STRUCT ```sql with data as ( select * from unnest ([ struct(1 as id, 'a' as label), struct(2, 'b'), struct(3, null) ]) ) select * from data; ``` ### C. UNNEST + typed ARRAY<STRUCT<>> ```sql with data as ( select * from unnest ( array<struct<id int64, label string>> [ (1, 'a'), (2, 'b'), (3, null) ] ) ) select * from data; ``` ## 比較 | 観点 | A: UNION ALL | B: inline STRUCT | C: typed ARRAY<STRUCT<>> | | -------------- | --------------------------- | --------------------------- | ------------------------ | | 型宣言 | 暗黙 (先頭行から推論) | 暗黙 (先頭 STRUCT から推論) | **明示** | | 列名の宣言位置 | 1 行目のみ (順序依存) | 1 つ目の STRUCT のみ | 1 箇所 (型と同居) | | NULL 耐性 | 低 (`null` 列で型不定) | 低 (同上) | 高 (型で確定) | | 各行の見た目 | `select ..., ... union all` | `struct(val, val, val),` | `(val, val, val),` | | 行追加時の事故 | `union all` 付け忘れ | カンマ漏れ | カンマ漏れ | ## typed UNNEST を第一選択にする根拠 ### 1. 型安定性: NULL 混在時の型推論が安定する A と B は値の出現位置から型を推論する。先頭行や列全体が `null` だと推論が失敗するか、`STRING` のつもりが `INT64` で確定するなどのズレが起き、`CAST(NULL AS STRING)` のような型変換を逐一書いて回避するしかない。 C は `ARRAY<STRUCT<id int64, label string>>` で型を先に宣言するので、`null` の位置や数値型の意図に関係なく壊れない。 学習用途では「試しに `null` を混ぜる」「意図した精度で揃える」操作が頻出するため、この差は大きい。 ### 2. 保守性: 列名・型の単一ソース化が効く ダミーに列を 1 つ追加する場面 (例: テストの `user` ダミーに `is_admin boolean` 列を追加する) で、A は全行の `select` 文を書き換える必要がある。B も `struct(...)` の要素数を全行揃える必要があり、列名を持つのは先頭のみなので見落としが起きやすい。 C は `ARRAY<STRUCT<...>>` ヘッダを 1 箇所変えれば済み、データ部は純粋なタプル列挙のまま残る。変更箇所が 1 つに閉じる分、書き換え漏れや要素数不一致といった編集事故が起きにくい。 ### 3. 可読性: データ部が tuple だけで密度が上がる C のデータ部は `(1, 'a'),` のように構文ノイズが最小で、行数が増えるほど視覚的な揃いが 3 記法の中で際立つ。構文要素 (`select` / `struct` / `union all`) が行ごとに再出現しないため、データの内容そのものに目が向く。 ダミーの役割は「入力パターンの網羅」を読み手に伝えることなので、記法が視線を奪わないのは重要。 ## 他 2 記法が活きる場面 冒頭 3 条件のいずれにも該当しない場合 (行数 10 件以下・NULL なし・列構造が固定) なら、以下のいずれかで十分。 ### A (UNION ALL): ポータビリティ優先 `UNION ALL` は標準 SQL の範囲に収まり、Snowflake / PostgreSQL / DuckDB などへコピペしても動く。 クエリを複数 DB 間で共有する前提がある場合や、BigQuery 固有機能に依存したくない汎用的な SQL 入門記事では A を選ぶ意味がある。 ### B (inline STRUCT): 2-3 行の超軽量 ad-hoc 型を明示するほどでもない 2-3 行の即席検証 (例: `select * from unnest ([struct(1 as x, 'a' as y)])` の書き捨て) では B の手軽さが勝る。 ## 関連ノート - **[派生]** [[Polars で全ての列を文字列型として DataFrame を作成する方法]]: 自動型推論を抑制する設計判断を、CSV/Excel 読み込みの層で適用した類例