最近社内の勉強会でなるほどと思ったので試して備忘録として残しておく
色々調べた後だと頻出するイディオムだった感はある…
BigQueryで重複を除いて最新状態のデータを出力する方法
- データの取り込みもしくは入れ込む際に
timestamp
カラムを追加する timestamp
で降順、特定キーでグルーピング、分析関数ROW_NUMBER
を用いて特定のグループの中での行番号を出力- 行番号が1のものを取り出す
これだけで特定キーでのユニークを担保してレコードを抽出できる
WITH data AS( SELECT * FROM UNNEST(ARRAY<STRUCT<ts TIMESTAMP, item STRING, store STRING, sales INT64>> [ ("2021-05-01 00:00:00", "hoge", "A", 100), ("2021-05-02 00:00:00", "hoge", "A", 200), ("2021-05-02 00:00:00", "fuga", "A", 100), ("2021-05-01 00:00:00", "hoge", "B", 100), ("2021-05-01 00:00:00", "fuga", "B", 100), ("2021-05-02 00:00:00", "hoge", "B", 200) ]) ), numbered AS ( SELECT ts, item, store, sales, ROW_NUMBER() OVER (partition by item,store order by ts desc) as rn FROM data ) SELECT * FROM numbered WHERE rn = 1
サンプルでは
data
- サンプルデータを生成
numbered
- 特定キーでグループ化、tsでソートして行番号を出力
rn=1
の行を抜き出す
item
,store
でユニークな最新レコードのみを抽出できた
後から追加された想定のレコードに関してはsales
の値が200になっているはず
QUALIFYで抽出する
上記ではWITH句で一度分析関数を使用した結果を保持させたが最近はQUALIFY句でさっと取れるようになっている
WITH data AS( SELECT * FROM UNNEST(ARRAY<STRUCT<ts TIMESTAMP, item STRING, store STRING, sales INT64>> [ ("2021-05-01 00:00:00", "hoge", "A", 100), ("2021-05-02 00:00:00", "hoge", "A", 200), ("2021-05-02 00:00:00", "fuga", "A", 100), ("2021-05-01 00:00:00", "hoge", "B", 100), ("2021-05-01 00:00:00", "fuga", "B", 100), ("2021-05-02 00:00:00", "hoge", "B", 200) ]) ) SELECT ts, item, store, sales, ROW_NUMBER() OVER (partition by item,store order by ts desc) as rn FROM data WHERE TRUE QUALIFY rn = 1
QUALIFYは少なくともWHERE
,GROUP BY
,HAVING
のどれかが必要
特に指定するものがない場合はWHERE TRUE
ですべてのレコードを対象としている
参考: 標準 SQL のクエリ構文 | BigQuery | Google Cloud
まとめ
とにかくデータを突っ込んで重複などを許す状態であってもSQLでよしなにできることがわかった
この方法で中間テーブルを生成してユニークにして集計してみたいな感じで処理できる
今までBigQueryは使っていてもRDB脳でやってたなーと反省した