dbt incremental modelとの格闘記録
はじめに
こんにちは、misatonです。ペパボのデータ基盤チームでディレクターをしています。
先日、dbtでincrementalモデルを初めて実装しました。
さらっとマテリアライゼーションの1つのように思っていたのですが、思ったよりtableやviewと勝手が違って手こずりました。
備忘録として、遭遇した問題と解決方法をまとめておきたいと思います。
今回の題材は売上データのモデリングです。
作成したモデル
| table_name | 説明 | type | materialization | |
|---|---|---|---|---|
| A | fct_account_daily_sales | アカウント別日次売上 | ファクトテーブル | incremental |
| B | dim_accounts | アカウント | ディメンションテーブル | incremental |
1.そもそも増分更新のイメージが貧弱だった
はじめにテーブルBについて、「一度でもアクティブだったことがある」という条件でこのように書きました。
-- テーブルB: dim_accounts
SELECT
account_id,
account_name,
account_url,
CURRENT_TIMESTAMP() AS updated_at,
FROM
source_table
WHERE
is_active = TRUE
また、このモデルにはaccount_idがuniqueであることを検証するテストを書いています。すると...
レビュー「これだと毎回アクティブなアカウントを全件取ってくるので、uniqueテストに引っかかるど」
そうでした。
incrementalは既存テーブルに追加していきます。
この書き方だと同じaccount_idが何度もINSERTされてしまい、テーブル全体としてはaccount_idがuniqueにならないですね。
解決: is_incremental()で分岐
あくまで既存レコードと重複のないaccount_idだけ追加したいので、is_incremental()マクロを使って、初回と2回目以降の実行で処理を分岐させることにしました。
-- テーブルB: dim_accounts
WHERE
is_active = TRUE
{% if is_incremental() %}
AND account_id NOT IN (
SELECT account_id FROM {{ this }}
)
{% endif %}
- 初回実行: 全アクティブアカウントを記録
- 2回目以降: 新規アカウントのみ追加
これでdim_accountsテーブルのユニークテストが失敗しなくなりました!やったね🌵
2.カラム名を変更するとどうなる? 失敗する
次はPull Request作成後、レビューを受けて取り込み作業をしていたときのことです。
一度テーブルをビルドしたあとにカラム名を変更すると、このエラーに当たりました。
エラー: Name old_column_name not found inside DBT_INTERNAL_SOURCE
なぜエラーになるか
dbtのincrementalモデルでは、on_schema_changeのデフォルト設定がignoreです。この設定では、
- カラムを削除するとエラーになる
- カラムを追加すると無視される
ref. incremental modelのデフォルト動作
カラム名の変更は以下の流れでエラーになります。
- 既存テーブル:
old_column_nameを持っている - 新しいSQL:
new_column_nameを返す - dbtは既存テーブルのカラム定義でMERGE文を生成するため、
old_column_nameを探しに行く - 結果: 新しいSQLには
old_column_nameがないのでエラー
tableやviewは完全再作成(DROP → CREATE)なのでカラム名変更も問題ないですが、incrementalでは既存テーブルを保持するため、スキーマ不整合が問題になります。
解決: --full-refresh で再作成するか、既存テーブルを削除して再作成。開発中にカラム名を変更する場合は、full refreshが必要でした(これちょっとめんどくさいね...)
3.カラムを削除するとどうなる? 失敗する
最後もon_schema_changeの話です。
Pull Requestレビュー中、すでにビルドした既存のテーブルからカラムを削除するcommitを積むとdbt run が失敗しました。
2で触れたとおり、incremental modelはデフォルト設定(on_schema_change: ignore)でカラムの追加は無視され、カラムの削除は失敗します。
スキーマの変更を無視されないようにするには、オプショナルな設定を行う必要があります。
on_schema_changeの設定
on_schema_change: sync_all_columnsを設定すると、次回実行時に新しいカラムを追加し、存在しないカラムを削除します。データの型(データ型)の変更も考慮されます。
ref. What if the columns of my incremental model change?
# テーブルA: fct_account_daily_sales
config:
materialized: incremental
on_schema_change: sync_all_columns
unique_key: ['sale_date', 'account_id', 'payment_category']
参考
- dbt - Incremental models
- What if the columns of my incremental model change?
- incremental modelのデフォルト動作
まとめ
- ユニークテストを入れる場合は
is_incremental()マクロでの条件分岐が必要 - デフォルトでは
on_schema_change: ignoreになっていて、カラム追加は無視され、カラム削除は失敗する - スキーマ変更を自動同期するには
on_schema_change: sync_all_columnsと設定する必要がある - デフォルト設定のままカラム名を変更するときは、
--full-refreshで再作成するか既存テーブルを削除する必要がある
ここまで踏んだら「カラムを追加するとどうなる?無視される」も踏んでおきたかったな。
なんにせよこれで今度からバンバンincremental modelを作れそうです。やったね🌵
Discussion