incremental + merge は、DWH 上での標準的な upsert(更新+挿入)パターンを実現するための dbt の中核機能です。
本稿では、unique_key 設計、重複排除、ウォーターマークの安全な取り方、パフォーマンスとスキーマ変更対応まで、試験で問われやすい論点と実務の勘所をまとめます。
dbt の incremental モデルで incremental_strategy=merge を使うと、ターゲット表に対し「キー一致時は更新、未一致時は挿入」という upsert が実行されます。これによりフルリロードを避け、到着データのみを効率的に反映できます。
merge を成立させる前提は一意なキー(unique_key)の適切な指定と、増分対象の正しい抽出です。試験観点では、unique_key が未設定または重複時のリスク(不定更新・MERGE エラー)を説明できることが重要です。
| 戦略 | 更新を反映 | 削除対応 | unique_key 必須 |
|---|---|---|---|
| merge | 可(キー一致で更新) | 原則不可(ソフトデリート推奨) | 必須 |
| append | 不可(常に追記) | 不可 | 不要 |
| insert_overwrite (partition) | パーティション単位で置換 | 可(対象分は置換) | 不要 |
| delete+insert | 可(対象を削除後に再挿入) | 対象範囲のみ可 | 必須 |
UPSERT フロー(incremental + merge)
最小の merge インクリメンタル設定
{{ config(
materialized='incremental',
incremental_strategy='merge',
unique_key='id'
) }}
with src as (
select * from {{ source('app', 'orders') }}
{% if is_incremental() %}
where updated_at >= (select coalesce(max(updated_at), '1970-01-01') from {{ this }})
{% endif %}
)
select * from srcmerge 成功のカギは unique_key の健全性です。ビジネスキーが揺れる場合はサロゲートキーの導入を検討し、ソースに重複が届く前提で前段での重複排除を徹底します。
試験では「unique_key が重複したら?」に対して、事前のウィンドウ関数による行選択(最新優先など)で 1 レコードに確定させる回答が期待されます。
| キー種別 | 長所 | 注意点 |
|---|---|---|
| ビジネスキー(自然キー) | 業務的に理解しやすい | 値の変更・再利用に弱い |
| サロゲートキー(代替キー) | 安定・衝突が少ない | 生成・同期の仕組みが必要 |
| 複合キー | 既存粒度を保てる | 列追加でクエリやテストが煩雑 |
重複排除の流れ
ウィンドウ関数で重複を 1 行に確定
with staged as (
select *,
row_number() over (
partition by id
order by updated_at desc
) as rn
from {{ source('app', 'orders') }}
), dedup as (
select * from staged where rn = 1
)
select * from dedup遅延到着や時刻の揺れに備えるため、単純な max(updated_at) ではなく、少し巻き戻した閾値で取り込むルックバック方式が安全です。ターゲット側の最大更新時刻から一定期間戻し、その範囲のソースを再評価します。
この方式では一部の重複取込が発生しうるため、前段の重複排除と unique_key の merge が前提になります。
| 方式 | 利点 | リスク/コスト |
|---|---|---|
| 単純 MAX 方式 | 最速・最小スキャン | 遅延到着を取り逃す可能性 |
| ルックバック併用 | 遅延に強い・堅牢 | 多少の再処理・重複評価が増える |
| ソース側変更履歴テーブル | 完全再現性・差分明確 | 履歴の提供/保管コストが必要 |
ルックバックの概念
ルックバック付き incremental + merge の完全例
{{ config(
materialized='incremental',
incremental_strategy='merge',
unique_key='id'
) }}
{% set lookback_hours = 24 %}
with base as (
select * from {{ source('app', 'orders') }}
), window as (
select coalesce(
dateadd('hour', -{{ lookback_hours }}, max(updated_at)),
cast('1970-01-01' as timestamp)
) as watermark
from {{ this }}
), filtered as (
select b.*
from base b
{% if is_incremental() %}
where b.updated_at >= (select watermark from window)
{% endif %}
), dedup as (
select *, row_number() over (partition by id order by updated_at desc) as rn
from filtered
)
select * from dedup where rn = 1大規模テーブルでは、物理設計(パーティション/クラスタ)と増分時の絞り込みがクエリコストに直結します。dbt の config でアダプタに応じた設定を行い、スキャン範囲を最小化します。
incremental_predicates は、ターゲット/ソース双方に適用される追加述語の指定に対応したアダプタで有効です。サポート状況は利用アダプタのドキュメントを確認してください。
| 最適化ポイント | 効果 | 留意点 |
|---|---|---|
| partition_by(日付) | スキャン削減・insert_overwrite と親和 | アダプタ依存の構文差異に注意 |
| cluster_by/ソートキー | 結合・MERGE の一致探索を高速化 | 過度な列数は書込コスト増 |
| incremental_predicates | 対象範囲のさらなる絞り込み | アダプタ対応状況の確認が必要 |
プルーニングのイメージ
最適化の例(アダプタ依存の一例)
{{ config(
materialized='incremental',
incremental_strategy='merge',
unique_key='id',
partition_by={'field': 'order_date'}, -- e.g. BigQuery/Spark family
cluster_by=['id'], -- e.g. BigQuery/Snowflake clustering equivalents
incremental_predicates=["order_date >= dateadd('day', -7, current_date)"]
) }}
select * from {{ ref('stg_orders_clean') }}
{% if is_incremental() %}
where order_date >= dateadd('day', -7, current_date)
{% endif %}増分モデルではスキーマ変更時の挙動を on_schema_change で制御します。append_new_columns は新規列を追加、fail は不整合時に失敗、sync_all_columns は対応アダプタで列差分を同期します。
実データの削除対応は、merge の標準挙動だけでは網羅できない場合があります。実務では is_deleted フラグでのソフトデリートが安全で、履歴の整合性も保ちやすいです。ハードデリートを行う場合は、影響範囲の正確な同定とテストを必須にしてください。
| on_schema_change | 挙動 | 利用シナリオ |
|---|---|---|
| ignore | 無視(差分あっても実行継続) | 短期回避・後で整備 |
| append_new_columns | 新規列を追加 | 列追加のみが想定される運用 |
| fail | 差分検知で失敗 | 厳密なスキーマ管理が必要な環境 |
| sync_all_columns | 列差分を同期(対応アダプタのみ) | 双方向で列変更を吸収したい場合 |
ソフトデリートの流れ
ソフトデリートを含む upsert ロジック(フラグ反映)
{{ config(
materialized='incremental',
incremental_strategy='merge',
unique_key='id',
on_schema_change='append_new_columns'
) }}
with src as (
select id,
updated_at,
status,
case when status = 'deleted' then true else false end as is_deleted
from {{ ref('stg_orders_clean') }}
{% if is_incremental() %}
where updated_at >= (select coalesce(max(updated_at), '1970-01-01') from {{ this }})
{% endif %}
)
select * from src試験では、incremental の前提(unique_key、is_incremental の役割、full_refresh の意味)と、merge を選ぶべき状況の判断が頻出です。on_schema_change の選択肢、重複排除の書き方、ウォーターマークの安全策まで説明できるように準備しましょう。
テスト(not_null/unique・関数テスト)をモデルに付与し、データ品質を担保するのも評価対象になりやすいポイントです。
| 問われやすいテーマ | 誤答の典型 | 正答の要点 |
|---|---|---|
| unique_key の必要性 | append でも更新される | merge はキー一致で更新、append は追記のみ |
| ウォーターマーク | max(created_at) を使う | updated_at とルックバックで遅延を吸収 |
| スキーマ変更対応 | 常に ignore | 要件に応じて append_new_columns / fail / sync_all_columns |
試験イメージ
品質テストの最小例(schema.yml 抜粋)
version: 2
models:
- name: dim_orders
columns:
- name: id
tests:
- not_null
- unique
- name: updated_at
tests:
- not_nullAnalytics Engineer
問題 1
dbt の incremental モデルで incremental_strategy=merge を用いる際、更新と新規挿入を正しく反映するための設計として最も適切なのはどれか。
正解: C
merge では unique_key が必須で、同一キーの複数候補は前段で一意に確定させる必要があります。遅延到着に備えて updated_at とルックバックを併用すると取りこぼしを防げます。append は更新を反映せず、on_schema_change はスキーマ差分の挙動でありキー重複を解決しません。
merge 戦略で削除(ソースに存在しない行の削除)も自動反映されますか?
標準的な upsert では削除は自動反映されません。監査要件がある場合は is_deleted のようなフラグでソフトデリートを反映するのが一般的です。ハードデリートが必要な場合は、範囲を正確に絞った delete+insert やパーティション単位の insert_overwrite(対応アダプタのみ)を検討してください。
BigQuery/Spark/Snowflake/Databricks での設定差はどう扱えばよいですか?
partition_by や insert_overwrite などはアダプタ依存のため、利用基盤のアダプタドキュメントに従います。dbt モデルでは共通部分(unique_key、is_incremental、重複排除)を確実に実装し、物理設計は config で分岐または環境別に管理するのが安全です。
full_refresh のタイミングは?
大規模スキーマ変更、ウォーターマークの不整合修正、履歴の再構築が必要なときに実行します。build 実行時に --full-refresh を付与すると対象 incremental テーブルは再作成されます。
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)、設定優先度...