⚙️

dbtで毎日データが欠損無く入っていることをテストする方法を比べてみた

2024/12/06に公開

この記事は Commune Advent Calendar 2024シリーズ1と、dbt Advent Calendar 2024の 6日目の記事です。

dbtのincremental modelを使った、日々の集計データを追記する履歴テーブルを皆さん運用していると思います。
何かしらの理由により空振りしてしまったり、洗い替えをするときに空白の期間が発生した時にすぐに検知出来るテストが欲しいですよね。

そんな時にぴったりなものが dbt-expectationsexpect_row_values_to_have_data_for_every_n_datepart です。

ネット上に記事が7件、それも利用者の記事は0件と、とても知名度が低いので今回取り上げることにしました。
きっと1年後には皆が使っている事があたりまえなテストの1つになってるはずです。

インストール方法

packages.yml に下記の行を追加し、dbt depsコマンドを実行します。

packages.yml
packages:
  - package: calogica/dbt_expectations
    version: [">=0.10.0", "<0.11.0"]
    # <see https://github.com/calogica/dbt-expectations/releases/latest> for the latest version tag
  - package: calogica/dbt_date
    version: 0.10.1

次に、dbt_project.ymlvarsに下記の行を追加します。コロンが変なところにありますがtypoではありません。

dbt_project.yml
vars:
  'dbt_date:time_zone': "Asia/Tokyo"
  session_date: '{{ run_started_at.astimezone(modules.pytz.timezone("Asia/Tokyo")).strftime("%Y-%m-%d") }}'

設定パラメータ

このテストではdate_col列を対象にdate_partの単位で、過去から現在までの範囲内のすべてのデータが欠落無く連続していることをチェックします。
1つでも欠落している日付があれば、テストは失敗します。

  • (必須) date_col : 日付列の指定を行います。
  • (任意) date_part : 日付をどのような粒度で入れているか指定します。'day', 'month', 'year'から選べます。デフォルトは日次のdayです。
  • (任意) test_start_date と、test_end_dateを用いてテストする日付の範囲を指定できます。指定が無ければ最も若いデータが観測された日から現在までの連続値でテストします。この記事では、varで指定したsession_dateを用いて1日前までテストすることをお勧めしています。
  • (任意) row_condition : 特定の行をテストから除外できます。
  • (任意) exclusion_condition : 週末や休日の日付を除外するなど、除外するデータをさらに細かく制御できます。例えばDAYOFWEEK(transaction_date) IN (1, 7)と指定すると、テスト対象から週末を除外します。

設定サンプル

2018-08-06から、実行日の前日までの毎日1件以上レコードが存在し、欠損がないかテストする設定は以下の通りです。

schema.yml
version: 2
models:
  - name: example_cnt_daily
    data_tests:
      - dbt_expectations.expect_row_values_to_have_data_for_every_n_datepart:
          date_col: fct_date_jst
          date_part: day
          test_start_date: '2018-08-06'
          test_end_date: "{{ var('session_date') }}"

例えば欠損だらけの下記テーブルでテストすると、is_missing:trueとなっている行がエラーとなった日付としてリストされます。

類似のテスト手法とその違い

dbt_expectations.expect_row_values_to_have_recent_data

schema.yml
version: 2
models:
  - name: example_cnt_daily
    columns:
      - name: fct_date_jst
        description: 集計日(JST)
        data_type: DATE
        data_tests:
          - dbt_expectations.expect_row_values_to_have_recent_data:
              datepart: day
              interval: 1

これは、テストを実行したタイミングの丸めてない時刻から一定の時間以内のデータが存在することをテストします。以下のユースケースにフィットします。

  • source.ymlにて元データとなるログテーブルに対してテストする
  • 毎時間かそれ以上の高頻度で追記される履歴テーブル

1日単位で丸めたデータを入れてる日次集計テーブルでは、以下の挙動となり役に立ちません。
例えば、翌日朝6時の処理時点で既に24時間+6時間の30時間の開きが発生します。実行が1時間遅れたら31時間の開きが観測され、エラーとなってしまいます。

dbt_utils.recency

先ほどの物に似てますが、タイムスタンプの列を日付に丸めて比較できます。そのため当日のデータが入っているかのテストだけであれば、これで実現できます。

schema.yml
version: 2
models:
  - name: example_cnt_daily
    data_tests:
      - dbt_utils.recency:
          datepart: day
          field: timestamp(fct_date_jst)
          interval: 1
          ignore_time_component: true

まとめ

かゆい所に手が届く、とても便利なテストでしたね!
BIダッシュボードに連携しているマートテーブルには、dbt_expectations.expect_row_values_to_have_data_for_every_n_datepart のテストをすぐにでも取り入れましょう!
ついでに、dbt_project.ymlにvar: session_dateを定義するとさらに便利ですよー

コミューン株式会社

Discussion