プロンプトとコンテキスト
あるチームがDuckDBを使ってParquet形式の変更ファイルを読み込み、ローカルで顧客ディメンションと価格履歴を維持管理しています。MERGE INTOをどのような場面で使用すべきか、また重複キー、削除、SCD Type 2、再実行、リカバリをどのように処理するかを説明してください。
面接官が見ているポイント
マッチ述語、アクションの順序、ソースの一意性、トランザクション境界に関する理解に加え、SQLの利便性を品質管理、監査、ロールバックを備えた再実行可能なパイプラインへと昇華させる能力がチェックされます。
明確にすべき質問
ビジネスキー、イベント時刻の順序、履歴保持の要件を確認します。重複レコード、遅延レコード、削除レコードの扱い、および障害発生後にターゲットテーブルを再構築可能かどうかを明確にします。MERGEを自動的かつ厳密に一度だけ実行されるCDC(exactly-once)として扱わないように注意してください。
30秒の回答
「ステージング環境でビジネスキーとイベント時刻によって重複排除を行い、ターゲットキーごとに適用される変更が高々1つであることを確認した上で、トランザクション内でMERGEを実行します。最新状態のテーブルには一致時の更新(UPDATE)と不一致時の挿入(INSERT)を使用し、SCD Type 2テーブルでは古いバージョンをクローズして新しいバージョンを挿入します。削除と再実行には明示的なセマンティクスが必要です。各バッチは入力スナップショットとチェック結果を記録し、失敗した場合はロールバックして同じスナップショットを再試行します。」
ステップごとの詳細解説
ターゲットのセマンティクスを定義する
現在のスナップショットと履歴を分離します。カレントテーブルには最新の状態が必要であり、SCD Type 2にはvalid_from、valid_to、is_current、およびバージョン制約が必要です。
まず監査可能なステージングを構築する
ソースファイル、バッチID、読み込み時刻、行ハッシュを保持します。ビジネスキー、イベント時刻、ソースの優先度で重複排除を行い、解決できない重複は隔離(quarantine)します。
マッチ条件とアクションを設計する
変更される可能性のある属性ではなく、マッチ述語には安定したビジネスキーを使用します。遅延データが誤って削除されないよう、一致時の更新、不一致時の挿入、およびソースに一致しない場合の削除ポリシーを明示的に指定します。
SCD Type 2を処理する
変更されたバージョンを挿入する前にカレント行をクローズします。内容が同一である場合は新しいバージョンを作成しないようにします。制約または品質クエリによって、各キーに存在するカレント行が常に1行のみであることを保証します。
再実行性とトランザクションを保証する
バッチIDと固定された入力スナップショットにより、実行の再現性が確保されます。MERGE、監査ログ、バッチステータスを単一のトランザクションにまとめ、変化するファイルを再読み込みするのではなく、同じステージングデータを再試行します。
監視とロールバック
ソースとターゲットの挿入・更新・削除件数を比較し、孤立キー、重複したカレント行、日時の逆転をチェックします。事前のスナップショットまたは再構築パスを保持し、異常発生時にはバッチ単位でロールバックします。
模範回答
Parquetファイルをバッチメタデータとともにステージングに取り込み、ビジネスキーとイベント時刻で重複排除を行った後、行数、NULL値、削除率の品質ゲートを適用します。カレントテーブルには安定したキーを用いた更新/挿入を行い、履歴テーブルでは変更されたバージョンをクローズして新しいバージョンを挿入することで、各キーにつきカレント行が1つだけになるようにします。MERGE、監査、バッチステータスはトランザクションを共有し、失敗時は同一スナップショットで再試行します。バッチごとの差分や重複バージョンを監視し、必要に応じてバッチ単位でロールバックまたは再構築を行います。
よくある間違い
生ファイルを直接マージする
ソース行に重複があると結果が曖昧になったり二重更新が発生したりするため、まずステージング、重複排除、品質ゲートを実施する必要があります。
変更可能なカラムでマッチングを行う
顧客名が変更された場合、新規キーとして認識されてしまう可能性があります。安定したビジネス識別子でマッチングしてください。
SCD Type 2で挿入のみを行う
古いバージョンが有効なまま残り、クエリを実行した際にカレント行が複数返されてしまいます。有効期間と一意性を適切に維持してください。
リトライ時にファイルを再読み込みする
ファイルが変更されていたり、新しいファイルが追加されていたりする可能性があります。入力スナップショットとバッチIDを固定してください。
フォローアップの質問
1つのキーに対して2つの異なるイベントが同時に届いた場合はどうしますか?
イベント時刻、バージョン、またはソース優先度に関する明確なルールに基づいて1つを選択します。それが不可能な場合は、暗黙的に上書きするのではなく隔離します。
遅延して届いた削除レコードをどのように処理しますか?
削除イベントの時刻をターゲットのバージョンと比較してツームストーン(tombstone)を記録し、古い削除処理によって新しい更新が上書きされないようにします。
失敗したMERGEが部分的にコミットされていないことをどのように証明しますか?
MERGE、監査ログ、バッチステータスを単一のトランザクション内に保持し、失敗後にターゲットの件数とステータスを確認した上で、同じステージングスナップショットを再試行します。
どのような場合にMERGEを避けますか?
ほぼ全件の置き換え、複雑なマッチングロジック、またはクロスシステムのトランザクションの場合は、新しいテーブルを作成してアトミックにスワップすることで、行単位のアクションに伴う不確実性を排除します。