dbt

dbt 増分マテリアライゼーション入門: 増分取り込みの基本と試験対策

2026-04-19
NicheeLab編集部

dbtのincrementalマテリアライゼーションは、既存データを再処理せず新規・更新分だけを書き込むことで、処理時間とコストを下げる仕組みです。仕組み自体はシンプルですが、ユニークキー、更新検出ロジック、戦略(merge/append/insert_overwrite)の選択、スキーマ変更への対応までを一貫して設計しないと事故につながります。

本稿では、dbt公式ドキュメントの安定機能に基づき、増分の基本から、主要ウェアハウス(Snowflake/BigQuery/Databricks/Spark/Redshift)での実装の違い、試験で狙われやすいポイントまでを一気に整理します。

増分マテリアライゼーションの原理と前提

incrementalは、モデルをテーブルとして物理化し、以後の実行で新規・更新データのみをターゲット表に反映します。初回はフルビルド、2回目以降は差分のみを処理するのが基本動作です。

差分判定は、モデルSQL内のis_incremental()ブロックの条件、もしくはアダプタが提供する戦略(mergeやinsert_overwrite)で行われます。安全な運用のためには、ユニークキーと更新検出列の定義が肝になります。

  • 初回: 常にフルロード。指定戦略によりテーブル作成
  • 2回目以降: is_incremental()条件や戦略に応じて差分のみ反映
  • 再作成したい場合: dbt run --full-refresh を明示的に使用
マテリアライゼーションストレージ消費再ビルド時間の目安
viewゼロ(計算時のみ)高速(常時クエリ評価)
tableフル再計算が必要で重い
incremental差分のみで短い
ephemeralゼロ上流にインライン展開(依存先の負荷次第)

増分ビルドの概念フロー

SourcesStaging(model A)Incremental(model B)Target TableMerge / Append / Insert_OverwriteSources → Staging → Incremental → Target Table (Merge/Append/Insert_Overwrite)

最小構成: incrementalモデルの雛形

{{ config(
    materialized='incremental'
) }}

with src as (
  select * from {{ ref('stg_orders') }}
)

select * from src
{% if is_incremental() %}
  -- 2回目以降にだけ適用される差分条件をここに記述
  -- 例: updated_atがターゲットの最大値以降
  where updated_at >= (
    select coalesce(max(updated_at), '1970-01-01') from {{ this }}
  )
{% endif %}

実装パターン: is_incremental と unique_key の設計

更新検出は代表的に2通りあります。タイムスタンプの水位(最大updated_at)で切る方法、あるいはユニークキーで衝突行をマージ(UPSERT)する方法です。前者は単純で高速、後者は重複や遅延到着データに強いのが特徴です。

dbtではunique_keyを設定し、incremental_strategy='merge'を選ぶと、サポートされるアダプタではMERGE文相当でのUPSERTが自動生成されます。is_incremental()のwhere条件とMERGEの双方を組み合わせ、読み取り量と整合性のバランスを最適化します。

  • 水位方式: updated_atやingested_atの最大値をターゲットから参照
  • キー方式: unique_keyを指定し、MERGEでUPSERT
  • ハイブリッド: whereで最近分だけ読み、MERGEで最終整合
方式利点注意点
水位(タイムスタンプ)単純・速い・スキャン削減時計ずれ/遅延到着で取りこぼしリスク
ユニークキー+MERGE重複や更新上書きに強いキー未定義や不安定キーは不可
ハイブリッドI/O削減と整合性の両立where漏れ時はfull-refreshが保険

is_incremental の分岐イメージ

is_incremental()?差分フィルタを適用Yes初回フルスキャンNo書き込み

MERGEを使うUPSERTテンプレート

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

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

select * from src

戦略別の挙動: merge / append / insert_overwrite

incremental_strategyは、アダプタ(ウェアハウス)の能力に合わせて選びます。サポート状況はバージョンにより変わるため、実環境のアダプタドキュメントを確認してください。以下は安定的に知られる傾向です。

mergeはユニークキーが必要で、更新・挿入を同時に扱えます。appendは常に追記のみで、重複排除は上流のSQLで担保が必要です。insert_overwriteはパーティション単位で置換でき、BigQueryやSpark系でよく使われます。

  • Snowflake/Redshift: mergeとappendが中心
  • BigQuery: mergeに加え、insert_overwriteをパーティションと併用可能
  • Databricks/Spark: mergeが主流、近年はinsert_overwrite(パーティション)も選択肢
戦略主な対応エンジン例要件/注意点
mergeSnowflake, BigQuery, Databricks/Spark, Redshiftunique_key必須。更新と挿入を同時処理
append全般重複は自身で排除。更新は不可(上書きなし)
insert_overwriteBigQuery, Databricks/Sparkpartition_byが前提。対象パーティションを置換

戦略選択のざっくりフロー

ユニークキーある?はいmergeいいえ追記のみでOK?はい: appendいいえ/日次置換: insert_overwrite

戦略ごとの設定例

-- Snowflake: MERGE
{{ config(materialized='incremental', incremental_strategy='merge', unique_key='id') }}

-- BigQuery: INSERT_OVERWRITE(日次分のみ置換)
{{ config(
  materialized='incremental',
  incremental_strategy='insert_overwrite',
  partition_by={'field': 'ds', 'data_type': 'date'},
  cluster_by=['customer_id']
) }}

-- Spark/Databricks: MERGE
{{ config(materialized='incremental', incremental_strategy='merge', unique_key='id') }}

スキーマ変更と再実行: on_schema_change と full-refresh

本番運用では列の追加・型変更が起こり得ます。dbtはon_schema_changeで基本動作を制御できます。サポート内容はアダプタに依存するため、より安全な選択肢をデフォルトにするのが無難です。

破壊的変更(型縮小や列削除など)や、ロジックの大幅変更時は、dbt run --full-refreshでの再作成が確実です。

  • ignore: スキーマ差分を無視(列は追加されない)
  • append_new_columns: 互換な新規列を追加(対応アダプタで有効)
  • sync_all_columns: 既存列のリネーム等も同期(対応状況に注意)
  • fail: 差分があれば失敗して気付ける
設定値挙動の概要使いどころ
ignore何もしないデータ品質優先で保守的にしたい時
append_new_columns新規列は追加列追加が頻発し後方互換な場合
sync_all_columns差分を同期スキーマ追随を自動化したい場合(要アダプタ対応)
fail差分で失敗CIで検知・手当てを強制したい場合

スキーマ進化時の分岐

Schema diff?ありon_schema_change ルール適用実行 / 失敗なし通常の増分処理

on_schema_change と full-refresh の例

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

-- 破壊的変更時はコマンドで再作成
-- dbt run --select my_model --full-refresh

パフォーマンス設計: パーティション・クラスター・水位管理

増分はI/O削減が主目的です。ウェアハウスのパーティション/クラスタリング機能と組み合わせると、読み取り範囲をさらに狭められます。特にinsert_overwriteはパーティション設計とセットで考えます。

水位は遅延到着分を考慮し、わずかに余裕を持たせた範囲で読むのが実務の定石です。たとえばmax(updated_at)から数時間〜1日戻すなど、環境に合わせてチューニングします。

  • partition_by/cluster_byでスキャン削減
  • max(updated_at)から少し巻き戻す安全マージン
  • ハイブリッド(差分where + MERGE)でI/Oと整合性のバランス
エンジン推奨の併用設定備考
BigQuerypartition_by(ds), cluster_by(user_id)insert_overwriteと相性が良い
Snowflakeクラスタリングキー(必要に応じて)コストと相談で設定
Databricks/SparkpartitionBy, Z-Ordering(環境依存)MERGEの対象範囲を絞る

水位管理のイメージ

Time ----|----|----|---->
          ^max_in_target
       <---- safety window ----
読み取り範囲 = [max_in_target - safety, now]

安全マージン付き水位の例(マクロ併用)

{% macro watermark(model, col, safety_hours=6) %}
select datetime_sub(max({{ col }}), interval {{ safety_hours }} hour) as wm
from {{ model }}
{% endmacro %}

-- モデル内
with w as (
  {{ watermark(this, 'updated_at', 6) }}
), src as (
  select * from {{ ref('stg_orders') }} s
  {% if is_incremental() %}
  where s.updated_at >= (select coalesce(wm, '1970-01-01') from w)
  {% endif %}
)
select * from src

試験対策チェックリストと落とし穴

Analytics Engineer試験では、incrementalの基本動作、unique_keyの必要性、on_schema_change、full-refresh、戦略選択の是非が狙われやすいです。文章問題では、どの戦略を選ぶべきか、どんな設定が不足しているかを問われます。

実務では、意図しない重複や取りこぼし、スキーマ進化時の黙殺が典型的な障害要因です。CIでfull-refreshも混ぜた検証、データプロファイルの監視をセットにするのが安全です。

  • unique_keyがないのにmergeを選んでいないか
  • insert_overwrite時にpartition_byを忘れていないか
  • on_schema_changeの既定値で差分が黙殺されていないか
  • full-refreshが必要な変更を増分で押し通していないか
キーワードよくある誤答/落とし穴正解の指針
mergeunique_key不要だと思い込むmergeには安定したunique_keyが要る
insert_overwrite任意の表で常に使えると誤解パーティション列が前提。対象分だけ置換
on_schema_change常にsync_all_columnsで安全アダプタ対応を確認。安全側に倒す

戦略選定の意思決定ミニツリー

更新を上書きする必要?はいunique_key ある?はい: mergeいいえ: キー導入検討いいえ追記だけでよい: append日次置換: insert_overwrite

検証と運用のコマンド例

# 差分のみ
 dbt run --select tag:incremental
# フル再作成(影響小さい単位で)
 dbt run --select my_model --full-refresh
# テストと組み合わせ
 dbt test --select state:modified+

問題で確認

Analytics Engineer

問題 1

BigQuery上の日次パーティション(ds列, DATE)を持つファクト表を、過去分は不変・当日分のみ差し替えたい。処理時間を最小化しつつ重複や取りこぼしを避ける推奨設定はどれか。

  1. materialized='incremental', incremental_strategy='insert_overwrite', partition_by={'field': 'ds', 'data_type': 'date'}
  2. materialized='incremental', incremental_strategy='append' のみ
  3. materialized='incremental', incremental_strategy='merge', unique_key未設定
  4. materialized='table' にして毎回フル再作成

正解: A

日次パーティションの当日分のみを置換する要件には、BigQueryのinsert_overwriteが適合します。partition_byの定義が必要です。appendは重複や当日更新に弱く、mergeにunique_key未設定は不適切、tableのフル再作成は不要に重いです。

よくある質問

unique_keyは常に必要ですか?

いいえ。merge戦略では必須ですが、appendやinsert_overwriteのみで済む設計(不変・追記専用、またはパーティション置換)なら必須ではありません。ただし更新上書きが必要なら安定したunique_keyを用意してください。

遅延到着データで取りこぼしが起きないようにするには?

水位をmax(updated_at)から数時間〜1日戻して読む、もしくは差分whereで直近期間だけ読み、mergeで最終整合を取るハイブリッドが有効です。取りこぼしが疑われる場合は一時的に範囲を広げるかfull-refreshで整合を回復します。

on_schema_changeの安全な既定値はどれですか?

運用方針次第ですが、まずはfailかignoreで保守的にし、列追加時は明示的にappend_new_columnsへ切り替えるのが無難です。sync_all_columnsは便利ですがアダプタ対応と変更内容を確認してから有効化してください。

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

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.