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 などを正しく判断できることが重要です。
インクリメンタル更新の高レベルフロー
最小構成のインクリメンタル雛形(境界条件は後述で具体化)
{{ 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 %}incremental_strategy はモデル更新の物理動作を決めます。merge はアップサート、append は単純追記、delete+insert は対象キーを削除後に挿入、insert_overwrite はパーティション単位の置換を行います。選定はデータ特性(更新頻度・遅延・削除発生・キー有無)と、実行基盤のアダプタ機能に依存します。
以下の比較表は試験・実務の双方で役立つ観点(変更検出方式、要件、長所、注意点、代表的なアダプタサポート)を網羅しています。
| 戦略 | 変更反映 | 要件(キー/パーティション) | 長所 |
|---|---|---|---|
| merge | UPSERT(更新/挿入/削除同等の扱いが可能) | 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 は行の一意識別に用いられ、merge と delete+insert では実質必須です。複合キーは配列で指定できます。キーが不安定な場合は、安定したサロゲートキー(ハッシュ)を導入し、上流の揺らぎを吸収します。
insert_overwrite を使う場合、partition_by の粒度が実行コストを左右します。遅延到着の最大幅や下流SLAを考慮し、日次・時間次など適切な粒度を選びます。
on_schema_change はカラム追加や型変更に対する挙動を制御します。append_new_columns や sync_all_columns の可用性はアダプタ依存のため、事前にサポート状況を確認し、安全側に設計します。
代表的な設定例(複合キー + スキーマ変更対応)
{{ 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 は対象キー集合を正しく抽出できれば再実行に強いですが、集合が大きいとコストが膨らみます。
遅延到着に強い境界条件(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: 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 の運用も併せて検討します。
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 実装例です。更新と削除の両方を扱い、遅延到着にも強く、試験の解答としても模範的な構成です。最後に検証観点のチェックリストを付けます。
検証では、初回フルロード、再実行、遅延到着の再取り込み、キー重複時の期待結果、スキーマ変更時の挙動までを自動テストに含めると安定します。
売上明細の 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 設定はどれか?
正解: 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 で必要パーティションのみ置換すると更に効率的です。
NicheeLab編集部
データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。
dbt Model の基礎: SQL で定義する変換の最小単位
Analytics Engineer 向けに、dbt Model の定義、マテリアライゼーション、依存関係、インクリメン...
dbt Analytics Engineer 試験ガイド: 出題範囲・配点・申込の実務視点
dbt Analytics Engineer 認定の出題範囲、配点の考え方、申込から受験までの流れを、公式ドキュメントの...
dbt Cloud と dbt Core の違いと選び方:Analytics Engineer 試験に効く要点
dbt Cloud と dbt Core の機能差を、実務と資格対策の両面から整理。スケジューリング、IDE、RBAC、...
dbt プロジェクト構造ガイド: models / seeds / macros の実務レイアウト
Analytics Engineer 向けに、dbt プロジェクトのディレクトリ構造と命名規約、dbt_project....
dbt_project.yml の読み方:主要設定と命名を最短で掴む
dbt_project.yml の必須キー、命名解決(database.schema.identifier)、設定優先度...