[[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 読み込みの層で適用した類例