dbt

dbt インクリメンタル戦略を正しく選ぶ:merge / append / delete+insert / insert_overwrite

2026-04-19
NicheeLab編集部

dbt の incremental モデルは全再計算を避け、更新分だけを反映するための基本機能です。ただし incremental_strategy の選択を誤ると、遅延・重複・欠落・削除未反映などの典型的な不具合を招きます。

本稿では公式ドキュメントに基づく安定挙動を前提に、merge / append / delete+insert / insert_overwrite の違いと試験で問われやすい論点、さらに実務での設定・検証手順をまとめます。

インクリメンタルの前提と試験で問われる観点

dbt の incremental モデルは初回フルロード後、2回目以降は差分だけを処理します。差分の特定は is_incremental() と時間・ID・パーティションなどの境界条件の組み合わせで行います。選ぶ戦略により、重複排除や削除の反映方法、キーやパーティションの要件が異なります。

Analytics Engineer 試験では、どの戦略がどのユースケースに適合するか、unique_key や partition_by、on_schema_change の関係、そして再実行時の安全性が狙われがちです。特に削除をどう扱うか(append では不可)、キーが無い場合の選択、パーティション単位の置換が必要なケースでの insert_overwrite などを正しく判断できることが重要です。

  • is_incremental() は初回実行時は false、2回目以降に true
  • 戦略により unique_key の必須性が変わる(merge / delete+insert は通常必須)
  • パーティション設計は insert_overwrite の成否とコストに直結
  • on_schema_change はアダプタ依存で挙動が異なるため安全側に設計

インクリメンタル更新の高レベルフロー

Raw/SrcS3, GCSStagingdbt refIncremental ModelFactis_incremental() で戦略 (merge / append / delete+insert / insert_overwrite) を適用

最小構成のインクリメンタル雛形(境界条件は後述で具体化)

{{ config(
    materialized='incremental',
    incremental_strategy='merge',
    unique_key='id'
) }}

with src as (
  select * from {{ source('app', 'events') }}
)

select *
from src
{% if is_incremental() %}
-- 差分抽出条件(例:イベント日時や増分IDなど)
where event_ts > (select coalesce(max(event_ts), '1970-01-01') from {{ this }})
{% endif %}

4つの incremental_strategy の挙動比較と選定基準

incremental_strategy はモデル更新の物理動作を決めます。merge はアップサート、append は単純追記、delete+insert は対象キーを削除後に挿入、insert_overwrite はパーティション単位の置換を行います。選定はデータ特性(更新頻度・遅延・削除発生・キー有無)と、実行基盤のアダプタ機能に依存します。

以下の比較表は試験・実務の双方で役立つ観点(変更検出方式、要件、長所、注意点、代表的なアダプタサポート)を網羅しています。

  • 削除を反映したいなら merge か delete+insert
  • キーが無ければ append(ただし重複対策が必要)か、パーティション置換の insert_overwrite
  • パーティション設計が良ければ insert_overwrite は非常に速い
  • スキーマ変更時は on_schema_change の互換性を事前確認
戦略変更反映要件(キー/パーティション)長所
mergeUPSERT(更新/挿入/削除同等の扱いが可能)unique_key ほぼ必須もっとも汎用。遅延着信や更新に強い
append追記のみ(削除・更新は反映しない)キー不要最速でシンプル。ログ系に適合
delete+insert対象キーの削除後に再挿入(結果的に UPSERT)unique_key 必須MERGE 不要環境でも更新を再現可能
insert_overwrite指定パーティションの置換(差分ではなくブロック単位)partition_by 必須(適切な粒度)大規模でもパーティション単位で高速かつ安定

戦略別の最小サンプル設定

-- merge(汎用 upsert)
{{ config(materialized='incremental', incremental_strategy='merge', unique_key='id') }}

-- append(追記のみ)
{{ config(materialized='incremental', incremental_strategy='append') }}

-- delete+insert(キー削除→再挿入)
{{ config(materialized='incremental', incremental_strategy='delete+insert', unique_key='id') }}

-- insert_overwrite(パーティション置換)
{{ config(
  materialized='incremental',
  incremental_strategy='insert_overwrite',
  partition_by={'field': 'event_date', 'data_type': 'date'}
) }}

unique_key・partition_by・on_schema_change の要点

unique_key は行の一意識別に用いられ、merge と delete+insert では実質必須です。複合キーは配列で指定できます。キーが不安定な場合は、安定したサロゲートキー(ハッシュ)を導入し、上流の揺らぎを吸収します。

insert_overwrite を使う場合、partition_by の粒度が実行コストを左右します。遅延到着の最大幅や下流SLAを考慮し、日次・時間次など適切な粒度を選びます。

on_schema_change はカラム追加や型変更に対する挙動を制御します。append_new_columns や sync_all_columns の可用性はアダプタ依存のため、事前にサポート状況を確認し、安全側に設計します。

  • unique_key は ['id1','id2'] のような複合指定も可
  • insert_overwrite は partition_by なしでは本質的な利点が出ない
  • on_schema_change は ignore / append_new_columns / sync_all_columns 等がある(アダプタ依存)
  • スキーマ変更は本番前に dbt build とリハーサル実行で検証

代表的な設定例(複合キー + スキーマ変更対応)

{{ config(
  materialized='incremental',
  incremental_strategy='merge',
  unique_key=['order_id','line_no'],
  on_schema_change='sync_all_columns'  -- アダプタ対応状況に注意
) }}

with src as (
  select *,
         to_date(created_at) as order_date
  from {{ ref('stg_orders') }}
)

select * from src
{% if is_incremental() %}
where updated_at > (select coalesce(max(updated_at), '1970-01-01') from {{ this }})
{% endif %}

境界条件・遅延到着・再実行の安全策

is_incremental() の条件は、遅延到着ウィンドウを考慮して少し広めに切るのが定石です。例えば最大2日の遅延が見込まれるなら、ターゲットの最大日時から2日戻して再取り込みし、merge で整合させます。

append 戦略で遅延に備える場合は、上流で重複排除(dedupe)やウィンドウ再読込の補正処理を必ず用意します。delete+insert は対象キー集合を正しく抽出できれば再実行に強いですが、集合が大きいとコストが膨らみます。

  • 遅延ウィンドウの再読み込み + merge が最も安全
  • append 選択時は重複排除の別モデルを用意する
  • insert_overwrite はウィンドウに被るパーティションのみ置換
  • 再実行に備え idempotent(再実行しても同じ結果)な設計を徹底

遅延到着に強い境界条件(2日戻しの例)

{{ config(materialized='incremental', incremental_strategy='merge', unique_key='event_id') }}

with src as (
  select * from {{ source('app', 'events') }}
), tgt as (
  select date_add('day', -2, max(event_ts)) as boundary from {{ this }}
)
select s.*
from src s
left join tgt t on 1=1
{% if is_incremental() %}
where s.event_ts >= coalesce(t.boundary, '1970-01-01')
{% endif %}

基盤別の実装注意(Snowflake / BigQuery / Databricks)

Snowflake: merge が安定。micro-partition による自動クラスタリング環境では、日時でのフィルタ条件を活用すると I/O が下がります。insert_overwrite 相当の動きは戦略としては一般的ではなく、日次テーブル分割や clone を別途検討します。

BigQuery: merge も insert_overwrite も一般的。insert_overwrite は partitioned table が前提で、指定したパーティション範囲のみ書き換えられるため大規模でも効率的です。クラスタリング列の適切な設定がスキャン削減に有効です。

Databricks/Delta(dbt-spark/dbt-databricks): merge が最有力。insert_overwrite は動的パーティション上書きモードが有効であることが前提です。大量アップサート時は Z-Order などの最適化や VACUUM の運用も併せて検討します。

  • Snowflake: merge + 適切なフィルタで安定運用
  • BigQuery: partition_by は必須級の設計要素。insert_overwrite が強力
  • Databricks: Delta の merge は信頼性高いが小さなファイル断片化に注意(オプティマイズ運用)

BigQuery 向け insert_overwrite の例(日付パーティション)

{{ config(
  materialized='incremental',
  incremental_strategy='insert_overwrite',
  partition_by={'field': 'event_date', 'data_type': 'date'},
  cluster_by=['user_id']
) }}

with src as (
  select *, cast(event_ts as date) as event_date from {{ ref('stg_events') }}
), window as (
  -- 直近3日だけを置換する例
  select generate_date_array(date_sub(current_date(), interval 2 day), current_date()) as dates
)
select s.*
from src s
join unnest((select dates from window)) d on d = s.event_date
{% if is_incremental() %}
-- 置換対象のパーティションのみ抽出
where s.event_date in (select d from unnest((select dates from window)) as d)
{% endif %}

完成例と検証チェックリスト

以下は売上明細の merge 実装例です。更新と削除の両方を扱い、遅延到着にも強く、試験の解答としても模範的な構成です。最後に検証観点のチェックリストを付けます。

検証では、初回フルロード、再実行、遅延到着の再取り込み、キー重複時の期待結果、スキーマ変更時の挙動までを自動テストに含めると安定します。

  • 初回と2回目以降でレコード件数・ハッシュが一致するか
  • 遅延到着データを手動投入して再実行時の整合を確認
  • 削除イベント(is_deleted 等)がある場合の反映を確認
  • スキーマ変更時に on_schema_change の期待通りの挙動か

売上明細の merge 実装(削除・更新・遅延対応)

{{ config(
  materialized='incremental',
  incremental_strategy='merge',
  unique_key=['order_id','line_no'],
  on_schema_change='append_new_columns'
) }}

with src as (
  select *,
         to_timestamp(updated_at) as up_ts
  from {{ ref('stg_order_lines') }}
), boundary as (
  select date_add('day', -1, max(up_ts)) as b from {{ this }}
)
select s.*
from src s
left join boundary b on 1=1
{% if is_incremental() %}
where s.up_ts >= coalesce(b.b, '1970-01-01')
{% endif %}

-- 削除フラグを採用する場合はモデル内でフィルタせず、
-- merge 条件と WHEN MATCHED THEN UPDATE/DELETE のテンプレートは
-- アダプタの既定に従います(dbt が生成)。

問題で確認

Analytics Engineer

問題 1

日次パーティション event_date を持つ超大規模イベントテーブルを、直近3日間だけ差し替えたい。重複や削除も正しく反映したい。もっとも適切な dbt 設定はどれか?

  1. materialized='incremental', incremental_strategy='insert_overwrite', partition_by=event_date
  2. materialized='incremental', incremental_strategy='append' のみ
  3. materialized='table' にして毎回フルリビルド
  4. materialized='incremental', incremental_strategy='merge', unique_key なし

正解: A

パーティション単位での差し替えには insert_overwrite が最適。partition_by を指定し、置換対象のパーティションのみを抽出する。append は削除・更新を反映できず不適、毎回フルビルドは不要な高コスト、merge はキー未指定では安全でない。

よくある質問

unique_key が無い場合、どの戦略を選ぶべきですか?

更新や削除を反映する必要がなければ append。更新が必要でパーティションが適切に設計できるなら insert_overwrite を検討します。merge / delete+insert は通常 unique_key が前提です。

on_schema_change は常に sync_all_columns を使えば安全ですか?

アダプタによりサポート状況と挙動が異なるため一概には言えません。まずは ignore か append_new_columns で安全側に倒し、検証環境での動作確認後に sync_all_columns を採用してください。

遅延到着に強いがコストも抑えたい場合の定石は?

境界条件を遅延ウィンドウ分だけ戻して再読込し、merge で整合させるのが定石です。パーティションを設けられるなら insert_overwrite で必要パーティションのみ置換すると更に効率的です。

この記事で学んだ内容を問題で確認しましょう

16,000問以上の問題で実力チェック

無料で問題を解いてみる
この記事の著者

NicheeLab編集部

データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。


関連記事
dbt

dbt Model の基礎: SQL で定義する変換の最小単位

Analytics Engineer 向けに、dbt Model の定義、マテリアライゼーション、依存関係、インクリメン...

dbt

dbt Analytics Engineer 試験ガイド: 出題範囲・配点・申込の実務視点

dbt Analytics Engineer 認定の出題範囲、配点の考え方、申込から受験までの流れを、公式ドキュメントの...

dbt

dbt Cloud と dbt Core の違いと選び方:Analytics Engineer 試験に効く要点

dbt Cloud と dbt Core の機能差を、実務と資格対策の両面から整理。スケジューリング、IDE、RBAC、...

dbt

dbt プロジェクト構造ガイド: models / seeds / macros の実務レイアウト

Analytics Engineer 向けに、dbt プロジェクトのディレクトリ構造と命名規約、dbt_project....

dbt

dbt_project.yml の読み方:主要設定と命名を最短で掴む

dbt_project.yml の必須キー、命名解決(database.schema.identifier)、設定優先度...

dbtの記事一覧 (101件)
© 2026 NicheeLab All rights reserved.