はじめに
以前、TRUNCATE + INSERTをMERGE文に置き換えた経験について書きました。今回はその前提となる、なぜTRUNCATE + INSERTには参照整合性上のリスクがあり、MERGEがそれを回避できるのかという仕組みの部分と、両者の使い分けの考え方を整理します。
BigQueryにおけるDML文の基本:各文は自動的にトランザクションになる
BigQueryのDML文(INSERT、UPDATE、DELETE、MERGE、TRUNCATEなど)は、それぞれの文が実行されるたびに、暗黙のトランザクションとして扱われます。つまり、1つのDML文は成功すればその内容がまとめて反映され、失敗すればその文単体がロールバックされます。
ここで重要なのは、これはあくまで**「1つの文」単位**での話だということです。TRUNCATEを実行してからINSERTを実行する、という2つの別々の文で構成した場合、それぞれが個別の暗黙トランザクションとして扱われます。TRUNCATEの完了とINSERTの開始の間には、明示的にトランザクションで囲まない限り、他のクエリから見て「テーブルが空になっている」瞬間が存在することになります。
明示的なトランザクションで囲むという選択肢
BigQueryは BEGIN TRANSACTION 〜 COMMIT TRANSACTION という形で、複数の文にまたがる明示的なトランザクションもサポートしています。これを使えば、TRUNCATEとINSERTを1つのトランザクションとして囲み、両方が成功して初めて変更が反映される、という形にすることも可能です。
ただし、明示的なトランザクションで複数の文を囲むということは、その分トランザクションが完了するまでの時間が長くなることを意味します。同じテーブルに対して他の更新処理が同時に走るような環境では、トランザクション同士が競合し、ロック待ちやリトライが発生する可能性が高くなります。トランザクションを使えば参照整合性の問題は解決できますが、その代わりに同時実行制御の複雑さが別の形で持ち込まれる、というトレードオフがあります。
MERGEが持つ「単一文での原子性」
一方、MERGE文は、INSERT・UPDATE・DELETEに相当する操作を1つのDML文の中で原子的に実行します。これは、TRUNCATE + INSERTのように2つの文に分かれているわけではなく、最初から1つの文として完結しているということです。
そのため、MERGEを使えば、
- 明示的なトランザクションで複数の文を囲む必要がない(そもそも1文で完結している)
- 参照側から見て、更新前か更新後かのどちらかの一貫した状態しか見えない
という形で、TRUNCATE + INSERTが抱えていた「テーブルが空になる瞬間」の問題を、トランザクションを使わずに解消できます。
使い分けの考え方
以上を踏まえると、大まかな使い分けの考え方は次のようになります。
- 参照整合性を重視したい(他のテーブルやクエリから常に一貫した状態を見せたい)場合:MERGEで1文にまとめる、あるいはTRUNCATE + INSERTを明示的なトランザクションで囲む
- 同時実行される他の更新処理との競合が懸念される場合:明示的なトランザクションよりも、単一文で完結するMERGEの方が扱いやすいことが多い
- 単純に全件入れ替えたいだけで、参照整合性やロックの懸念がほとんどない場合:シンプルさを優先してTRUNCATE + INSERTのままでも問題にならないこともある
また、MERGEを使う場合は、WHEN NOT MATCHED BY SOURCE THEN DELETE 句を入れるかどうかで、「sourceに存在しないデータをtargetから削除するか、残すか」という挙動が変わります。TRUNCATE + INSERTと同じ「完全な入れ替え」を再現したい場合は、この句を含める必要があります。
まとめ
- BigQueryのDML文は、それぞれが暗黙のトランザクションとして扱われるため、TRUNCATEとINSERTを別々の文として実行すると、その間にテーブルが空になる瞬間が生じる
- 明示的なトランザクションで囲めばこの問題は解決できるが、トランザクションの時間が延びることで同時実行の競合リスクが増す
- MERGEは複数の操作を1つの文で原子的に行うため、トランザクションを使わずに参照整合性の問題を回避できる
- 完全な入れ替えを再現したい場合は
WHEN NOT MATCHED BY SOURCE THEN DELETEを含めるかどうかの判断が必要になる
「とりあえずTRUNCATE + INSERT」ではなく、参照整合性・同時実行・実装のシンプルさのどれを優先するかによって、適切な方式を選ぶことが重要だと感じています。