append は「既存行を一切更新せず、新規行だけを追記する」最速の増分戦略です。イベントログや監査証跡など不変データに最適ですが、設計を誤ると重複や取りこぼしが発生します。
本稿では dbt の安定機能に基づき、実務で安全に使うための最小要件とパターン、そして Analytics Engineer 試験で問われやすい論点をまとめます。
append は、初回は全量作成、2回目以降は is_incremental() ブロックで絞り込んだ新規レコードだけを INSERT します。既存行の更新・削除は一切行いません。
よって、データが実質的に不変(immutable)であるか、もしくはアップサートを不要とする要件でのみ採用すべきです。更新が必要な場合は merge 等の別戦略を選びます。
| 戦略 | 基本動作 | 更新/削除対応 | 主な適用シナリオ |
|---|---|---|---|
| append | 新規行のみ INSERT | 不可 | イベントログ、不変監査証跡、追記型 CDC ストリーム |
| merge | キー一致は更新/挿入、条件次第で削除 | 可(アダプター依存) | ディメンションのアップサート、最新状態の整備 |
| insert_overwrite | パーティション単位で上書き | 限定的(単位内の置換) | BigQuery 等での日次/時間分割の再生成 |
append 戦略のデータフロー(単純追加)
append 戦略の最小構成(イメージ)
{{ config(materialized='incremental', incremental_strategy='append') }}
with src as (
select * from {{ ref('stg_events') }}
{% if is_incremental() %}
-- 新規レコードだけを通すフィルタ(後述で具体例)
{% endif %}
)
select * from src;dbt の増分モデルは、初回実行時は is_incremental() が偽のため全量 SELECT が実行され、次回以降は真となり、開発者が書いた WHERE 句で新規行だけを抽出します。append では unique_key は使用されません(指定してもアップサートは行われません)。
最重要はウォーターマーク列の選定です。一般的に単調増加するタイムスタンプ(created_at や _ingested_at)の最大値を {{ this }} 側で参照し、それより新しいソース行のみを取り込みます。遅延到着を考慮してセーフティウィンドウ(例: 2 日巻き戻し)を取るのが実務的です。
ウォーターマーク参照の基本パターン
{{ config(materialized='incremental', incremental_strategy='append') }}
with src as (
select * from {{ ref('stg_events') }}
{% if is_incremental() %}
where created_at > coalesce((select max(created_at) from {{ this }}), '1970-01-01')
{% endif %}
)
select * from src;以下は運用に耐える最小構成例です。on_schema_change の挙動はアダプター依存のため、対応状況を確認のうえ使用してください。サポートされる場合、append_new_columns は新規列を追記し、既存行には NULL を入れます。
ステージングでの事前重複排除と、ターゲット側での境界列フィルタを併用するのが安全です。
append 戦略の運用テンプレート(Snowflake/Databricks/BigQuery いずれでも概念同じ)
{{ config(
materialized='incremental',
incremental_strategy='append',
on_schema_change='append_new_columns', -- アダプターがサポートする場合
tags=['incremental','append']
) }}
with deduped as (
select
id,
created_at,
payload,
row_number() over (partition by id, created_at order by _ingested_at desc) as rn
from {{ ref('stg_events') }}
), src as (
select id, created_at, payload from deduped where rn = 1
{% if is_incremental() %}
-- 2 日巻き戻して遅延到着をカバー(取り込み重複は上流で排除)
where created_at >= dateadd(day, -2,
coalesce((select max(created_at) from {{ this }}), '1970-01-01')
)
{% endif %}
)
select id, created_at, payload from src;append は更新できないため、遅延到着や同一行の再送があると簡単に重複が生じます。これを避けるには、上流での重複排除+セーフティウィンドウの再取り込みを組み合わせます。取り込み窓を広げると再取り込み量は増えますが、重複はステージングで排除できます。
dbt のテストでは、ターゲット表に対して完全一意キーを強制するのではなく、現実的な組み合わせ(例: id, created_at)に一意制約テストをかけるのが有効です。
schema.yml の一意性テスト例(ターゲット)
version: 2
models:
- name: fct_events_append
tests: []
columns:
- name: id
tests: [not_null]
- name: created_at
tests: [not_null]
tests:
- dbt_expectations.expect_compound_columns_to_be_unique:
column_list: ['id','created_at']append では既存行を直せないため、上流起因の欠損・型誤りを是正するときはフルリフレッシュが有効です。--full-refresh は対象モデルを再作成します。
スキーマ変更は on_schema_change のサポート状況に従い設定します。未対応のアダプターではフルリフレッシュで反映させるのが安全です。
よく使うコマンド
# 単一モデルを増分実行
dbt run -s fct_events_append
# タグでバッチ実行
dbt run -m tag:append
# フルリフレッシュ
dbt run -s fct_events_append --full-refreshAnalytics Engineer 試験では、append の適用条件、is_incremental の役割、unique_key の扱い、フルリフレッシュの意味などの基礎が頻出です。merge との違いを文章で正確に説明できるかを意識してください。
特に「append は既存行を変更しない」「unique_key を指定してもアップサートされない」「遅延到着への対策が必要」という3点はセットで問われやすいです。
誤り例と正解例(コンセプト)
-- 誤り: append で unique_key を当ててもアップサートされない
{{ config(materialized='incremental', incremental_strategy='append', unique_key='id') }}
-- 正解: append では is_incremental() で新規行を定義
{{ config(materialized='incremental', incremental_strategy='append') }}
{% if is_incremental() %}
where created_at > (select max(created_at) from {{ this }})
{% endif %}Analytics Engineer
問題 1
dbt の incremental モデルで append 戦略を用いる場合、正しい記述はどれか。
正解: C
append は新規行を追記するのみでアップサートはしません。unique_key は使用されず、遅延到着や重複への対策は is_incremental() の条件やステージングで設計します。--full-refresh は有効で、テーブルを作り直します。
append 戦略で unique_key を設定するとどうなりますか?
append では unique_key は使用されません。既存行の更新や重複解消は行われないため、アップサートが必要な場合は merge 戦略を選択してください。
遅延到着データはどう扱えば安全ですか?
is_incremental() の WHERE で最大境界から数日分巻き戻すセーフティウィンドウを設け、ステージングで row_number などにより重複排除してからターゲットへ append するのが実務的です。
スキーマ変更(列追加)が起きたらどうなりますか?
アダプターが on_schema_change=append_new_columns などをサポートしていれば、新規列を追加できます。未対応または複雑な変更時は --full-refresh で再作成するのが安全です。
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)、設定優先度...