dbtで毎日データが欠損無く入っていることをテストする方法を比べてみた
この記事は Commune Advent Calendar 2024シリーズ1と、dbt Advent Calendar 2024の 6日目の記事です。
dbtのincremental modelを使った、日々の集計データを追記する履歴テーブルを皆さん運用していると思います。
何かしらの理由により空振りしてしまったり、洗い替えをするときに空白の期間が発生した時にすぐに検知出来るテストが欲しいですよね。
そんな時にぴったりなものが dbt-expectations の expect_row_values_to_have_data_for_every_n_datepart です。
ネット上に記事が7件、それも利用者の記事は0件と、とても知名度が低いので今回取り上げることにしました。
きっと1年後には皆が使っている事があたりまえなテストの1つになってるはずです。
インストール方法
packages.yml
に下記の行を追加し、dbt deps
コマンドを実行します。
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.yml
のvars
に下記の行を追加します。コロンが変なところにありますがtypoではありません。
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件以上レコードが存在し、欠損がないかテストする設定は以下の通りです。
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
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
先ほどの物に似てますが、タイムスタンプの列を日付に丸めて比較できます。そのため当日のデータが入っているかのテストだけであれば、これで実現できます。
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