青の統計学-DS Playground-

incremental:速い更新と lookback の trade-off

Stage 3 — 第5章 | dbt入門カリキュラム 推定学習時間:40〜50分 | 難易度:★★★★☆


この章で学ぶこと

第4章 で日次 Activity mart を学びました。 多くの mart は full refresh 可能な table ですが、行数が増えると build 時間と warehouse コストが問題になります。

dbt の incremental モデル は、2 回目以降の build で差分のみ更新します。 ただし lookback 期間と full-refresh 手順を設計しないと、静かに古い数字が残る リスクがあります。

まず dbt の作法を整理し、続けて DS Playground の fct_activity_events_daily_by_type を読み解きます。

この章を終えると、こんなことができるようになります:

  • incremental モデルの config ブロックを読める
  • なぜ 14 日 lookback が必要か説明できる
  • unique_keydelete+insert の関係を理解できる
  • ソース遅延・バックフィル時の運用判断を説明できる

なぜ incremental か

方式 動き 向いているケース
table(full) 毎回全期間再計算 中小規模、ロジック変更が多い
incremental 差分のみ更新 大規模 fact、遅延到着、コスト削減

テーブル・ビュー・MV で学んだ物理化の選択が、dbt では materialized config になります。

incremental を選ぶ典型理由は 3 つです。

  1. fact 行数が大きい … 毎回 full refresh すると warehouse コストが高い
  2. ソース遅延到着 … 過去日の行が後から修正・追加される
  3. 定期バッチの SLA … nightly build を短時間で終えたい

incremental は速さのための 近道 ですが、trade-off を理解した上で導入します。

flowchart TD A["初回 build
is_incremental() = false"] B["2 回目以降
lookback 内のみ再集計"] C["delete+insert
by unique_key"] D["lookback 外は
触らない"] A --> C B --> C --> D

incremental config の読み方

{{
            config(
              materialized='incremental',
              unique_key='...',
              incremental_strategy='delete+insert',
              on_schema_change='sync_all_columns'
            )
          }}
          
設定 典型値 意味
materialized incremental 2 回目以降は差分 build
unique_key grain に一致するキー 行の一意識別子
incremental_strategy delete+insert 対象キーを削除してから再 INSERT
on_schema_change sync_all_columns 列追加時の CI 挙動

unique_keygrain と一致 させる必要があります。 grain が 1 日 × 1 activity_type なら、キーも (date_day, activity_type) の合成列にします。


lookback の設計

incremental run では is_incremental() ブロック内で 直近 N 日だけ を upstream から読みます。

{% if is_incremental() %}
            where date_day >= (
              select coalesce(max(date_day), current_date) - interval 'N days'
              from {{ this }}
            )
          {% endif %}
          

lookback を設ける理由:

  1. ソース遅延 … 同期やアプリ修正で過去日の行が更新される
  2. current-state テーブルの影響 … イベント履歴ではないソースでも、日付列の修正は起きうる
  3. 安全マージン … 運用上のバッファ(7 日では足りないケースをカバー)

lookback 内のキーは delete+insert上書き されます。 lookback より古い日 は incremental run では 触られません — ここが full-refresh が必要になる境界です。


--full-refresh が必要なとき

シナリオ incremental のみ full-refresh
昨日の activity 修正 lookback 内なら通常 build で OK
lookback 外の過去日修正 古い行が更新されない 必要
新列追加(schema change) sync_all_columns で対応可 場合により
ロジック変更(定義変更) 要検討 推奨

incremental 導入時のチェックリスト:

  1. unique_key は grain と一致するか
  2. lookback はソース遅延より長いか
  3. full-refresh 手順を runbook に書いたか
  4. 定義変更時に historical 再計算が必要と README に書いたか
  5. singular test が incremental 後も通るか

fct_activity_events_daily_by_type

DS Playground では fct_activity_events_daily_by_type が、教学用の incremental mart 例になっています。 int_user_activity_events から 日 × activity_type で集計する mart です。

grain: 1 行 = 1 日 × 1 activity_type

モデル config

{{
            config(
              materialized='incremental',
              unique_key='activity_daily_key',
              incremental_strategy='delete+insert',
              on_schema_change='sync_all_columns',
              tags=['product_health', 'active_users', 'incremental_example', 'no_pii']
            )
          }}
          
設定 意味
materialized incremental 2 回目以降は差分 build
unique_key activity_daily_key 行の一意キー(date_day:activity_type
incremental_strategy delete+insert 対象キーを削除してから再 INSERT
on_schema_change sync_all_columns 列追加時の CI 挙動

14 日 lookback の SQL

with activity_events as (
            select *
            from {{ ref('int_user_activity_events') }}

            {% if is_incremental() %}
              where date_day >= (
                select coalesce(max(date_day), current_date) - interval '14 days'
                from {{ this }}
              )
            {% endif %}
          ),

          daily_activity_by_type as (
            select
              date_day,
              activity_type,
              count(*) as activity_records,
              count(distinct user_id) as active_users,
              min(occurred_at) as first_activity_at,
              max(occurred_at) as last_activity_at
            from activity_events
            group by 1, 2
          )

          select
            concat(date_day::text, ':', activity_type) as activity_daily_key,
            ...
          from daily_activity_by_type;
          
flowchart TD A["int_user_activity_events"] --> B{"is_incremental()?"} B -->|No 初回| C["全期間集計"] B -->|Yes| D["直近 max(date_day) - 14日 以降のみ"] C --> E["delete+insert by activity_daily_key"] D --> E E --> F["fct_activity_events_daily_by_type"]

なぜ 14 日か

  1. ソース遅延 — Supabase 同期やアプリ修正で過去日の行が更新される
  2. 最新状態テーブルの影響problem_attempts はイベント履歴ではないが、日付列の修正は起きうる
  3. 安全マージン — 7 日では足りないケースをカバーする 運用上のバッファ

full-refresh が必要な場面

古い current-state ソース行が backfill または修正されたときは --full-refresh を使います。 30 日前の problem 状態修正は lookback 外のため、incremental のみでは 古い行が更新されません。 ロジック変更(定義変更)時も full-refresh が推奨されます。

CI では analytics_ci schema で build されるため、本番 analytics とは別です(Stage 5 第 3 章)。


出力列の読み方

用途
activity_records 行動イベント件数(同一ユーザーの複数行動含む)
active_users その type の distinct user
first_activity_at / last_activity_at 日次の時間範囲。異常検知に使える

fct_active_users_daily の type 別列と 定義は近い が、grain が type 別行 のため Metabase pivot に向きます。


よくある誤解

誤解 実際
「incremental = 常に速い・常に正確」 lookback 外の修正は残る。full-refresh が必要
「unique_key は飾り」 delete+insert の対象行を決める
「lookback を長くすれば万能」 コスト増。遅延実績に合わせて設計

体系コラム:カリキュラム上の位置づけ

項目 内容
Stage / 章 Stage 3 — 第5章
今回の論点 incremental config、14 日 lookback、delete+insert、full-refresh
前章との接続 Active User mart
次章への伏線 Monetization status — Stage 4

まとめ

incremental は差分更新で build 時間とコストを抑えますが、lookback 期間と full-refresh 手順が設計の要です。 unique_key と grain を一致させ、ソース遅延より長い lookback を設け、定義変更時は historical 再計算を runbook に書きます。

DS Playground では fct_activity_events_daily_by_type が教学例として incremental を実践し、14 日 lookback + delete+insert で product health の type 別推移を返しています。


関連教材


確認問題

問題 1

14 日 lookback の主な目的はどれですか。

A. PII を 14 日で自動削除する
B. 遅延到着・過去日修正を incremental 更新で吸収する
C. BI ツールのキャッシュ期限に合わせる
D. OLTP の課金を 14 日単位にする

正解: B


問題 2

30 日前の fact 行が修正されたとき、lookback なしの incremental build だけでは足りない理由はどれですか。

A. incremental は未来日しか処理しない
B. lookback 外の日は再集計されない
C. delete+insert は使えない
D. unique_key が無効になる

正解: B


問題 3

incremental_strategy='delete+insert'unique_key を組み合わせた incremental mart で起きることとして正しいのはどれですか。

A. 表全体が毎回 TRUNCATE される
B. 対象キー行が削除され、lookback 内が再 INSERT される
C. 重複キーが許容される
D. view に変換される

正解: B


用語メモ(この章)

用語 意味(この章での使い方)
incremental 差分のみ更新する materialization
lookback incremental run で再集計する直近 N 日
unique_key delete+insert の対象行を決める一意キー
delete+insert 対象キー削除後に再 INSERT する戦略
is_incremental() 2 回目以降の build かを判定する Jinja 関数
full-refresh 全期間を再計算する --full-refresh build