ARTICLE

dbt incremental の方式選定 — append/merge/insert_overwrite を切り分ける(2026年版)

2026/5/13

SHARE

dbt incrementalの4方式をテーブル種別とDWHで切り分ける選定フロー(2026年版)

db

dbt incremental の方式選定 — append/merge/insert_overwrite を切り分ける(2026年版)

ARTICLE株式会社renue
renue

株式会社renue

2026/5/13 公開

AI導入・DXの悩みをプロに相談してみませんか?

AIやDXに関する悩みがありましたら、お気軽にrenueの無料相談をご利用ください。 renueのAI支援実績、コンサルティングの方針や進め方をご紹介します。

dbt を導入した組織でしばらく経つと、「table マテリアライゼーションでは遅すぎる、全部 incremental にしたい」という声が出る。だが incremental には方式が複数あり、選定を誤ると重複行が積まれる、想定外のフルスキャンが走る、データが消えるといった事故が起きる。本記事では incremental の4方式 (append / merge / delete+insert / insert_overwrite) を、テーブル種別と data warehouse に紐づけて判定するフローを書く。

マテリアライゼーション4種の使い分け — view / table / incremental / ephemeral

incremental に踏み込む前に、dbt 公式ドキュメントが定義する4つのマテリアライゼーション (dbt Developer Hub「About incremental models」) の住み分けを確認する。

  • view: 軽量で常に最新だが、参照のたびに base クエリが走る。BI から直接参照される最終モデルには向かない。
  • table: 毎回フルリビルド。データ量が小さく、ビルド時間が許容できる中間モデルの初期値として最適。
  • incremental: 差分だけ追加更新する。データ量が大きく、フルリビルドの時間か warehouse コストが業務要件を破る段階で初めて検討する。
  • ephemeral: CTE に展開される中間ロジック。深い依存で SQL が肥大化するので、参照階層は浅く保つ。

大事なのは「incremental は早すぎる最適化になりやすい」という事実。table でビルドが数秒のうちは incremental に切り替えない、と決めておくと事故が減る。

append 方式 — 「重複が必ず起きる」前提で組む

最もシンプルな方式。{% if is_incremental() %} ブロック内で「last_updated > (SELECT MAX(last_updated) FROM {{ this }})」のように追加分だけ insert する。dbt はマッチング処理を持たないため、同じ行が2回現れたら2回 insert される。

append が向くのは、append-only な event ストリームに限る。clickstream / アプリログ / IoT センサーデータのような、行が一意に確定する性質のデータ。逆に上流のソースで何かのリトライが起きるとそのまま二重 insert になるため、最終 BI 出力側で QUALIFY ROW_NUMBER() OVER () による dedup を必ず仕込む。

merge 方式 — 変更が起きる dimension テーブルの定石

upsert (insert or update) が必要な dimension テーブル向けの方式。unique_key を指定すると MERGE INTO で既存行を更新する。dbt Developer Hub「unique_key」は「unique_key が "old" 側に存在しない場合は INSERT、存在する場合は UPDATE/REPLACE」と挙動を定義している。

merge には2つの落とし穴がある。1つ目、incremental_predicates を指定しないと、宛先テーブル全体をスキャンしてマッチを探す。Adrienne Vermorel 著「dbt Incremental Models: The Complete Guide」(2024年公開, adriennevermorel.com) は、行数が一定規模を超えると merge の性能劣化が顕著になると整理しており、預ける側で predicate を入れる重要性を強調している。2つ目、unique_key の cardinality が高いと merge cost 自体が膨らむ。dimension の自然キー (例: user_id, snapshot_date の複合) を選ぶ設計が結局効く。

delete+insert 方式 — Snowflake で期間まるごと置換したい時の選択

「過去N日分のデータをまとめて差し替えたい」要件に向く。dbt は DELETE と INSERT の2文を順に発行する。dbt Developer Hub「About incremental strategy」は Snowflake / Databricks / Postgres でこの strategy をサポートしている。

トレードオフは atomicity。DELETE と INSERT が別トランザクションのため、その間に失敗すると「削除済みだが未挿入」の状態でテーブルが取り残される。バックフィル中に運用が停まらないよう、別 schema にビルドしてから swap する deployment 設計をセットで持っておく。Adrienne Vermorel の比較記事 (2024年公開) では、行数規模が大きい Snowflake テーブルで merge より高速になる場合が紹介されている。具体的な倍率は warehouse サイズと unique_key 設計で変動するため、自前で EXPLAIN を取って判断するのが現実解。

insert_overwrite 方式 — BigQuery 上の fact テーブルの第一候補

BigQuery / Spark / Databricks で使える方式。dbt 公式 incremental-strategy ドキュメントは「partition 単位で『該当 partition 全体を一度削除して再挿入』する」と説明する。BigQuery のpartitioned tables 公式ドキュメント (Google Cloud)で前提となる partition 設計と組み合わせて初めて性能を発揮する。

insert_overwrite は unique_key を使わず、partition 単位で動く。これは行レベル比較を完全に回避できるという意味で、event log / 売上明細 / IoT メトリクスのような fact テーブルで最も性能とコストが見合う。バックフィル時にも安全で、append のような重複も発生しない。Paradime「Optimizing dbt Incremental Models for Performance」(paradime.io) も BigQuery では insert_overwrite をデフォルト推奨として位置付けている。

方式選定フローチャート — テーブル種別から決める

テーブルが何を表現するかから方式を逆引きする。

  • append-only な event ストリーム (clickstream / IoT / アプリログ): append または insert_overwrite (partition 必須)
  • 変更が起きる dimension (users / accounts / org_tree): merge + incremental_predicates
  • 期間まるごと差し替えたい fact (日次集計 / 月次レポート): BigQuery なら insert_overwrite、Snowflake なら delete+insert
  • window 関数で前後行を参照する集計 (cohort / retention): いったん table で完成させてから、必要なら incremental + lookback で組み替える

バックフィルと late-arriving facts — 「気づいたら過去データがズレている」を防ぐ

incremental モデルが運用 1年も回ると、上流の修正・遅延データ・スキーマ変更で「過去データが本来の値からドリフトしている」事象が起きる。dbt 公式は「Incremental models in-depth」でこの問題を「late-arriving facts」と呼び、「定期的な full-refresh の運用」を推奨している。

具体的には、incremental のサンプルウィンドウに lookback (例: 直近7日) を持たせて、その期間内に遅延データが来ても拾える設計にする。さらに月1か四半期1で --full-refresh を打って、累積したドリフトをリセットする運用を仕込む。insert_overwrite なら partition 範囲を指定して部分 full-refresh ができるため、運用コストが低い。

スキーマ変更時の挙動 — カラム追加で過去行が NULL になる

incremental モデルにカラムを追加すると、過去行ではそのカラムが NULL になる。dbt 0.21 以降は on_schema_change: append_new_columns を設定すると、次回ビルドから新カラムを反映するが、過去行の NULL を埋めるには --full-refresh が必要。スキーマ進化のタイミングで「いつ full-refresh するか」を chemicals が合意しておかないと、BI 側で気づいた時には数ヶ月分の NULL が積み上がっている。

モニタリング — 「incremental が効いていない」典型サイン

運用に入ったあとに観測すべきシグナル。

  • ビルド時間が --full-refresh と変わらない: incremental predicate が効いていない可能性。WHERE 句のカラムが partition と一致しているか確認
  • 行数が想定より増えない: 上流側で「更新」されているのに、predicate が「新規行」しか拾えていない。merge への移行か、lookback の追加を検討
  • merge cost が突発的に高騰: unique_key の cardinality を見直し、不要なカラムを merge から外す。BigQuery なら slot 使用量、Snowflake なら credit 使用量で気づく

renue で見ている dbt incremental の選び方 — 「partition + insert_overwrite」が現実解

BigQuery を使う組織で複数案件を見てきた中で、fact テーブルは insert_overwrite + partition の組み合わせで運用するのが運用負荷が低い。append は重複対策が常時必要、merge は cost 管理が難しいため、最初から partition 設計と insert_overwrite を選ぶ判断がコスト面で素直。

renue 社内でも、外部 API の繰り返し呼び出しでGCP課金が無駄に発生する課題があり、結果をDB側に正規化キャッシュとして保存し直す設計を Slack で議論している。incremental モデルが目指す「同じ計算を二度しない」の発想は、データウェアハウス内のクエリだけでなく、データソース取り込みのレイヤーまで貫通する設計判断につながる。

選定理由を schema.yml に書き残す — 3か月後の自分を救うドキュメント

incremental の方式選定は、半年経つと「なぜこの選択だったか」を本人も忘れる。schema.yml の model description に「fact_orders は売上明細で日次 partition × insert_overwrite。merge を選ばない理由はカーディナリティが大きく cost が膨らむため」のようにコメントとして残しておくと、新メンバーが触る時の事故が減る。dbt Developer Hub の incremental best practices も「choosing the right materialization is one of the most consequential decisions」と強調しており、この記録が運用品質の差を生む。

まとめ — incremental は「table で困ってから」始める

incremental は強力だが、運用コストもそれなりに伴う。table ビルドが業務要件を超えてから検討する、というルールを最初に置く。検討する時は本記事のフローで方式を決め、選定理由を schema.yml に残す。これだけで、データチームが3年後も触れる schema が残り、新メンバーの onboarding コストも下がる。

あわせて読みたい

AI活用のご相談はrenueへ

renueは553のAIツールを自社運用する「自社実証型」AIコンサルティングファームです。

→ AIコンサルティングの詳細を見る

SHARE

FAQ

よくある質問

tableマテリアライゼーションのフルリビルドが業務要件のビルド時間やwarehouseコストを破る段階で初めて検討する。データ量が小さくビルドが数秒で済むうちは、運用負荷の追加コストを払うメリットが薄い。

mergeは行レベルでunique_keyを使って既存行を更新する方式で、変更があるdimensionテーブルに向く。insert_overwriteはpartition単位で該当範囲を一度削除して再挿入する方式で、BigQueryのfactテーブルでコストと性能のバランスが良い。

appendはマッチング処理を持たないため、上流のリトライや遅延データで重複行が発生する。最終BI出力側でROW_NUMBERによるdedupを仕込むか、unique_keyを定義してmergeに切り替えるのが対策。

incrementalのサンプルウィンドウにlookback(例:直近7日)を持たせて遅延データを拾える設計にする。さらに月1〜四半期1で--full-refreshを打って累積ドリフトをリセットする運用を仕込む。insert_overwriteならpartition範囲指定で部分full-refreshが可能。

AI導入・DXの悩みをプロに相談してみませんか?

AIやDXに関する悩みがありましたら、お気軽にrenueの無料相談をご利用ください。 renueのAI支援実績、コンサルティングの方針や進め方をご紹介します。

関連記事

AI導入・DXの悩みをプロに相談してみませんか?

AIやDXに関する悩みがありましたら、お気軽にrenueの無料相談をご利用ください。 renueのAI支援実績、コンサルティングの方針や進め方をご紹介します。

無料資料をダウンロード

AI・DXの最新情報をお届け

renueの実践ノウハウ・最新記事・イベント情報を週1〜2通配信