dbt のインクリメンタルモデルは is_incremental() で「取り込むソース行」を絞れますが、ターゲット側(既存テーブル)への更新対象までは自動では絞り込みません。ここで効くのが incremental_predicates です。
本稿では、MERGE 時に対象行をさらに限定する incremental_predicates の設計ポイント、典型ユースケース、アダプター差分、試験で問われやすい観点をまとめます。
incremental_predicates は、インクリメンタルモデルの MERGE(またはアダプターが対応する同等戦略)で、ターゲット側に追加の絞り込み条件を与えるための設定です。実行時にはアダプターのマクロ内で、MERGE の対象(多くの場合、ターゲット表のエイリアス)に対する AND 条件として適用され、更新・削除の候補を限定します。
is_incremental() によるソース側の絞り込みだけでは、ターゲット側は全体をスキャンしがちです。特に巨大なパーティション/クラスターを持つ事実テーブルでは、MERGE のマッチ探索が高コスト・高ロックになりやすい。incremental_predicates により「影響し得るターゲット行」にまで範囲を狭めると、スキャン量・課金・ロック時間のいずれも下げられます。
| 手法 | どこで絞るか | 主用途 / 効果 | リスク・注意 |
|---|---|---|---|
| is_incremental() フィルタ | ソース側(staging から取り込む行) | 増分入力の削減。抽出・集計の軽量化 | ターゲット探索は別問題。MERGE 側のスキャンは残る |
| incremental_predicates | ターゲット側(MERGE の対象候補) | 更新・一致探索の縮小。パーティション/クラスタ pruning を促進 | 条件設計ミスで取りこぼし。アダプター差分の検証が必須 |
| DWH の分割機能(分区/クラスタ/索引) | ストレージ/エンジン | 物理的なスキャン削減の下支え | 定義・メンテナンスが必要。論理条件だけでは効かないこともある |
MERGE 時の対象縮小イメージ
基本形: リストで条件を渡す(アダプターの内部エイリアスを利用)
{{ config(
materialized='incremental',
unique_key='order_id',
incremental_strategy='merge',
-- 例: 影響し得るパーティションに限定(内部エイリアス名はアダプターで固定化されていることが多い)
incremental_predicates=[
"DBT_INTERNAL_DEST.order_date >= (select min(order_date) from DBT_INTERNAL_SOURCE)",
"DBT_INTERNAL_DEST.order_date <= (select max(order_date) from DBT_INTERNAL_SOURCE)"
]
) }}
select ... from {{ ref('stg_orders') }}
{% if is_incremental() %}
where updated_at >= (select max(updated_at) from {{ this }})
{% endif %}日付や時間で分割された巨大テーブルでは、増分ソースが触れる期間にだけターゲットを限定するのが定石です。ソース側の最小・最大の分割値を使い、ターゲットの同一範囲にだけ MERGE をかけます。
BigQuery/Snowflake/Databricks いずれも、分割列に対する述語は強力にプルーニングされます。is_incremental() で「新着のみ」を読みつつ、incremental_predicates で「対象パーティションのみ」を更新。二段構えにすることで、読み取りも書き込みも縮小できます。
例: BigQuery の日付パーティションを範囲指定
{{ config(
materialized='incremental',
unique_key='order_id',
incremental_strategy='merge',
partition_by={'field': 'order_date', 'data_type': 'date'},
incremental_predicates=[
-- SOURCE の最小/最大日付で TARGET 側パーティションを限定
"DBT_INTERNAL_DEST.order_date between (select min(order_date) from DBT_INTERNAL_SOURCE)
and (select max(order_date) from DBT_INTERNAL_SOURCE)"
]
) }}
select ... from {{ ref('stg_orders') }}
{% if is_incremental() %}
where order_date >= date_sub(current_date(), interval 7 day)
{% endif %}CDC(変更データキャプチャ)では、1 回の取り込みで変わるキーはごく一部です。そこで「今回触れたキー集合」を別モデル(エフェメラルでも可)で作り、ターゲット側をその集合に限定します。
これにより、大規模なターゲットの全スキャンを避けつつ、ピンポイントで MERGE が可能になります。
例: 今回触れた customer_id のみに限定
-- models/int_keys_this_run.sql (ephemeral)
{{ config(materialized='ephemeral') }}
select distinct customer_id
from {{ ref('stg_cdc_events') }}
{% if is_incremental() %}
where _ingested_at >= (select coalesce(max(_ingested_at), '1970-01-01') from {{ this }})
{% endif %}
-- models/fct_customers.sql (incremental)
{{ config(
materialized='incremental',
unique_key='customer_id',
incremental_strategy='merge',
incremental_predicates=[
"DBT_INTERNAL_DEST.customer_id in (select customer_id from {{ ref('int_keys_this_run') }})"
]
) }}
select ... from {{ ref('stg_customer_snapshot') }}取りこぼし防止は最優先です。遅延到着やアップサートの再送を見越し、incremental_predicates はソースの min/max など安全側の範囲で組むのが無難です。キー限定の場合も、再処理ウィンドウ(例: 過去 N 日のキーを常に含める)を設けると安心です。
スキーマ変更や列追加との相性にも注意します。MERGE で更新対象列を限定する場合は、merge_update_columns と on_schema_change を併用して整合性を保ちます。フルリフレッシュ時は incremental_predicates は無視されるため、全件再構築されます。
設定例: 更新列の限定とスキーマ変更方針
{{ config(
materialized='incremental',
unique_key='id',
incremental_strategy='merge',
merge_update_columns=['status', 'updated_at'],
on_schema_change='append_new_columns', -- アダプターにより選択肢は異なる
incremental_predicates=[
"DBT_INTERNAL_DEST.dt >= dateadd(day, -3, current_date)" -- Snowflake 例
]
) }}
select id, status, updated_at, dt from {{ ref('stg_events') }}incremental_predicates の適用位置や内部エイリアス名は、アダプター実装(BigQuery、Snowflake、Databricks/Spark など)に依存します。多くの実装では MERGE の WHEN MATCHED(あるいは対象テーブル別名)に AND 条件として差し込まれますが、戦略が insert_overwrite の場合などは無関係になることがあります。
本番前には必ず dbt compile で生成 SQL を確認し、該当述語が期待どおりにターゲット側に適用されているかを目視で検証します。さらに小規模データで A/B 実行して件数・スキャン量の差分を計測しておくと安全です。
検証のための基本コマンドとチェック項目
# コンパイルして SQL を確認
$ dbt compile -s models/fact_orders.sql
# 生成 SQL 中の該当箇所(例)
-- WHEN MATCHED AND (DBT_INTERNAL_DEST.order_date between ...)
-- が想定どおりに入っているか
# 小規模サンプルでの比較
$ dbt run -s fact_orders --vars '{sample: true}'
$ dbt run -s fact_orders --full-refresh # ベースライン比較
# メトリクス
-- 対象パーティション数、更新件数、スキャンバイト/時間is_incremental() はソース側の行を絞る。incremental_predicates はターゲット側の更新対象を絞る。役割の違いを明確に説明できるようにしましょう。
append 戦略や insert_overwrite 戦略では挙動が異なる点、MERGE 戦略における効果が本質である点を押さえる。パーティション列やキー集合による限定の設計パターンを、短い SQL で書けるようにしておくと実務・試験ともに有利です。
頻出パターンのテンプレート
{{ config(
materialized='incremental',
unique_key='order_id',
incremental_strategy='merge',
incremental_predicates=[
"DBT_INTERNAL_DEST.partition_date between (select min(partition_date) from DBT_INTERNAL_SOURCE)
and (select max(partition_date) from DBT_INTERNAL_SOURCE)"
]
) }}
select ... from {{ ref('stg_orders_incremental') }}
{% if is_incremental() %}
where partition_date >= date_sub(current_date(), interval 3 day)
{% endif %}Analytics Engineer
問題 1
巨大な日付パーティションの注文テーブルを MERGE 戦略でインクリメンタル更新しています。直近 7 日分のみ新規・更新がある想定で、MERGE のターゲット側もその範囲に限定してスキャン量を下げたい。最も適切な設定はどれか?
正解: B
ソース側の絞り込みだけでは MERGE のターゲット探索は広いままです。incremental_predicates でターゲット側も該当パーティションに限定するのが正解。on_schema_change や tag は目的に合致しません。
incremental_predicates は is_incremental() の代わりになりますか?
なりません。is_incremental() は取り込むソース行を絞り、incremental_predicates は MERGE のターゲット側の一致・更新対象を絞ります。両方を組み合わせて二段構えにするのが実務ベストプラクティスです。
append や insert_overwrite 戦略でも効果がありますか?
append(追記のみ)では更新が発生しないため incremental_predicates の効果はありません。insert_overwrite(特に BigQuery のパーティション上書き)は粒度設計が主眼で、incremental_predicates は通常参照されません。MERGE を使う戦略でこそ効果があります。
内部エイリアス DBT_INTERNAL_SOURCE/DEST を参照してよいですか?
多くのアダプターで安定的に使われますが、最終的にはアダプター実装に依存します。必ず dbt compile の生成 SQL を確認し、述語が期待どおりにターゲット側に適用されていることを検証してください。バージョン差分には注意しましょう。
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)、設定優先度...