0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【SQL】BigQueryで差分チェックを高速化する

Last updated at Posted at 2025-12-28

はじめに

FARM_FINGERPRINTは実際どのくらい速くなるのか?
BigQuery を使ったデータパイプラインで、ほぼ確実に出てくる処理があります。

tmp テーブルを取り込む
既存テーブルと比較して差分を検知
差分だけ UPDATE / INSERT(MERGE)

最初は素直に「各カラムを比較」して実装することが多いと思います。
しかし、行数やカラム数が増えてくると、急に処理が重くなることがあります。

この記事では、STRUCT + FARM_FINGERPRINT を使った差分チェックが、実務的にどのくらい効くのかを、BigQuery 視点で整理します。

よくある差分チェックの書き方

たとえば、こんな SQL を書いたことはないでしょうか。

WHERE
  t.col1 <> m.col1 OR
  t.col2 <> m.col2 OR
  t.col3 <> m.col3

この書き方のつらいところ

  • カラムが増えるたびに SQL が長くなる
  • NULL / 空文字の考慮が必要
  • STRING 比較が多く CPU コストが高い
  • OR 条件が多く、実行計画的にも不利

データ量が少ないうちは問題ありませんが、
100万行以上 × 多カラムになると、ここがボトルネックになりがちです。

行全体をハッシュ化して比較するという考え方

そこで使えるのが、次のパターンです。

FARM_FINGERPRINT(
  TO_JSON_STRING(
    (SELECT AS STRUCT t.* EXCEPT (A_TIMESTAMP))
  )
)
  • 何をしているか
  • 行全体を STRUCT にまとめる
  • 更新日時など、差分判定に不要なカラムを除外
  • JSON 文字列に変換
  • 64bit のハッシュ値(INT64)を生成

これにより、「行の中身」を 1 つの数値で表現できます。

tmp と main を比較して差分だけ SELECT する

WITH
main AS (
  SELECT
    id,
    FARM_FINGERPRINT(
      TO_JSON_STRING(
        (SELECT AS STRUCT m.* EXCEPT (A_TIMESTAMP))
      )
    ) AS row_hash
  FROM dataset.main_table m
),

tmp AS (
  SELECT
    id,
    FARM_FINGERPRINT(
      TO_JSON_STRING(
        (SELECT AS STRUCT t.* EXCEPT (A_TIMESTAMP))
      )
    ) AS row_hash
  FROM dataset.tmp_table t
)

SELECT
  COALESCE(t.id, m.id) AS id,
  CASE
    WHEN m.id IS NULL THEN 'INSERT'
    WHEN t.id IS NULL THEN 'DELETE'
    WHEN t.row_hash <> m.row_hash THEN 'UPDATE'
  END AS diff_type
FROM main m
FULL OUTER JOIN tmp t
  ON m.id = t.id
WHERE
  m.id IS NULL
  OR t.id IS NULL
  OR t.row_hash <> m.row_hash;

この SQL で、
INSERT
UPDATE
DELETE
すべての差分を一度に取得できます。
で、どのくらい速くなるのか?

結論から言うと、条件次第で 2〜10 倍以上速くなります。

実務でよく見る目安

行数 × カラム数 生カラム比較 row_hash比較
10万 × 5 ほぼ同じ ほぼ同じ
100万 × 20 15〜30秒 2〜5秒
1000万 × 30 数分 10〜20秒

※ データ型や STRING 比率により前後します

なぜ速くなるのか(BigQuery視点)
① 比較対象が INT64 1 列になる

WHERE t.row_hash <> m.row_hash
  • STRING 比較がない
  • 分岐が少ない
  • CPU 的に非常に軽い

② スキャンする列数が減る

BigQuery は I/O(スキャン量)支配です。

生カラム比較:複数列をスキャン
ハッシュ比較:row_hash 1 列だけ

この差が、大規模データではそのまま実行時間差になります。

注意:毎回ハッシュを計算するだけでは限界がある

WHERE
  FARM_FINGERPRINT(TO_JSON_STRING(...))
  <>
  FARM_FINGERPRINT(TO_JSON_STRING(...))

この書き方でも SQL は綺麗になりますが、

毎回 STRUCT → JSON → HASH を計算

CPU コストはそれなりにかかる

劇的な高速化にはなりません
(体感 1.5〜2 倍程度のことが多いです)

本当に速くしたい場合の設計
row_hash を物理カラムとして持つ
row_hash INT64

INSERT / UPDATE 時だけ計算

差分チェックは数値比較のみ

WHEN MATCHED
  AND m.row_hash <> s.row_hash
THEN UPDATE

これが BigQuery 差分更新の完成形です。

どんなときに使うべきか

おすすめできるケース

  • 100万行以上の定期バッチ
  • MERGE が遅くなってきた
  • カラム数が多いテーブル
  • 差分ロジックをシンプルに保ちたい
  • 不要なケース
  • 数万行以下の小規模データ
  • 単発処理
  • カラムが 2〜3 個しかない

まとめ

ハッシュ比較は万能ではない
ただし row_hash を保持する設計にすると非常に強力
BigQuery の差分処理は、SQL ではなく 設計で速くなる
差分チェックや MERGE の性能に悩んでいる場合、一度検討する価値は十分あります。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?