はじめに
「sourceの内容をそのままtargetテーブルに反映する」という処理がよく登場します。素朴に実装すると TRUNCATE TABLE してから INSERT する、いわゆるTRUNCATE INSERTの形になりがちですが、この方式には見落としがちな問題があります。
今回、TRUNCATE INSERTをMERGE文に置き換えた話と、その際に整理した「sourceにどこまで従うか」という設計判断についてまとめます。
TRUNCATE INSERTの問題点
TRUNCATE INSERTは実装がシンプルで分かりやすい一方、TRUNCATEした瞬間からINSERTが完了するまでの間、テーブルが空になるという瞬間が発生します。
このテーブルを他のテーブルやビュー、あるいは別のクエリが参照している場合、その空白のタイミングで読みに行くと、本来存在するはずのデータが0件として見えてしまう、といった問題が起こり得ます。
これを避けるためにトランザクションでTRUNCATEとINSERTを囲むという対策も考えられますが、トランザクションで囲むこと自体が別の問題(ロックの競合や、同時実行されている他の更新処理とのコンフリクトなど)を引き起こす可能性があり、必ずしも万能な解決策ではありません。
MERGEによる置き換え
そこで採用したのが、TRUNCATE INSERTの代わりに MERGE文一発でsourceの内容をtargetに反映する という方式です。
MERGEは単一のSQL文として実行されるため、TRUNCATE INSERTのような「テーブルが空になる瞬間」が発生しません。参照側から見て、常に更新前か更新後どちらかの一貫した状態しか見えないという点が大きなメリットです。
設計判断:「sourceにどこまで従うか」
MERGEに置き換える際に整理が必要だったのが、targetに存在するがsourceには存在しない行をどう扱うかという点です。ここで2つのパターンが考えられます。
パターン1:sourceを完全に信頼する(TRUNCATE INSERT相当)
sourceの内容がすべてであり、targetはsourceの完全なミラーであるべき、という考え方です。sourceから消えた(=削除された)データは、targetからも削除する必要があります。
MERGE INTO target_table T
USING source_table S
ON T.id = S.id
WHEN MATCHED THEN
UPDATE SET T.value = S.value
WHEN NOT MATCHED BY TARGET THEN
INSERT (id, value) VALUES (S.id, S.value)
WHEN NOT MATCHED BY SOURCE THEN
DELETE
WHEN NOT MATCHED BY SOURCE THEN DELETE を入れることで、sourceに存在しなくなった行をtargetから削除し、TRUNCATE INSERTと同じ「target = sourceの完全な写し」という状態を実現できます。
パターン2:sourceに存在しないデータには手を出さない
一方、targetのデータが必ずしもsourceの全量に依存しておらず、「sourceにある分だけ更新・追加すればよく、sourceに無いからといって既存データを消すべきではない」というケースもあります。この場合は WHEN NOT MATCHED BY SOURCE 句を書かず、更新と追加のみを行います。
MERGE INTO target_table T
USING source_table S
ON T.id = S.id
WHEN MATCHED THEN
UPDATE SET T.value = S.value
WHEN NOT MATCHED BY TARGET THEN
INSERT (id, value) VALUES (S.id, S.value)
この場合、sourceに存在しない行はtarget側にそのまま残り続けます。
まとめ
TRUNCATE INSERTをMERGEに置き換えることで、「テーブルが空になる瞬間」という参照整合性上のリスクを避けることができます。
ただし単純に書き換えれば良いわけではなく、「targetはsourceに完全に依存すべきか、それとも部分的な反映で良いのか」 という業務要件レベルの判断が必要になります。この判断によって WHEN NOT MATCHED BY SOURCE THEN DELETE を入れるかどうかが変わり、結果として出来上がるデータの性質(sourceの完全なミラーか、増分反映か)が変わってきます。
TRUNCATE INSERTからMERGEへの置き換えを検討する際は、この「sourceへの依存度」を最初に整理しておくと、実装で迷わずに済むと思います。