🌵

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']

参考

まとめ

  • ユニークテストを入れる場合はis_incremental()マクロでの条件分岐が必要
  • デフォルトでは on_schema_change: ignoreになっていて、カラム追加は無視され、カラム削除は失敗する
  • スキーマ変更を自動同期するにはon_schema_change: sync_all_columnsと設定する必要がある
  • デフォルト設定のままカラム名を変更するときは、--full-refreshで再作成するか既存テーブルを削除する必要がある

ここまで踏んだら「カラムを追加するとどうなる?無視される」も踏んでおきたかったな。

なんにせよこれで今度からバンバンincremental modelを作れそうです。やったね🌵

GMOペパボ株式会社

Discussion