dbt の Source freshness で loaded_at_field を省略してテーブルの鮮度判定を試してみる
はじめに
dbt の Source freshness では、loaded_at_fieldに時刻カラムを指定して鮮度判定を行うのが一般的ですが、loaded_at_fieldを省略してもテーブルメタデータを使った鮮度テストが可能です。
こちらを試してみた内容を本記事でまとめます。
Source freshness
Source freshness は、Source として定義したテーブルのデータが「どれくらい新しいか」を判定する dbt の機能です。warn_after/error_afterの閾値を超えてデータが更新されていない場合に、warn / error として検知できます。
本機能については以下に記載があります。
Source freshness 機能の主要なポイントは以下のとおりです。
loaded_at_fieldにカラムを指定すると、そのカラムの最大値と現在時刻の差で鮮度を判定loaded_at_fieldを省略した場合、アダプタが提供するメタデータベースの判定が使用される- Snowflake では
INFORMATION_SCHEMA.TABLES.LAST_ALTEREDが参照されます
- Snowflake では
- 閾値は
warn_after/error_afterで設定
loaded_at_fieldの省略は、執筆時点で Snowflake, Redshift, BigQuery (dbt-bigquery version 1.7.3 以降), Databricks (dbt Fusion engine)でサポートされています。
loaded_at_field省略時の挙動
loaded_at_fieldを省略した場合、Snowflake ではINFORMATION_SCHEMA.TABLESビューのLAST_ALTEREDカラム(テーブルへの最終 DML/DDL 時刻)が鮮度判定に使われます。
この方式には以下のような特性があります。
- 鮮度判定用のカラム(
updated_atなど)をテーブル側に持っていなくても利用可能 LAST_ALTEREDはTIMESTAMP_LTZのため、後述の NTZ 起因の誤判定が発生しない- 一方で、DML 他に DDL やバックグラウンドでのメタデータ処理によって最後に変更された日時も記録されるため、業務的な意味でのレコードのタイムスタンプとは別物になる点に注意します
ここでは、以下のパターンで動作を確認します。
| # | パターン | loaded_at_field |
|---|---|---|
| 1 | メタデータベース | 省略 |
| 2 | NTZ 値を指定 | updated_at |
| 3 | 2 のカラムをCONVERT_TIMEZONEで明示的に UTC 変換 |
convert_timezone('Asia/Tokyo', 'UTC', updated_at) |
前提条件
検証環境
以下の環境を使用しています。
- DWH:Snowflake
- dbt
- dbt platform
- Fusion Stable リリーストラック
テストテーブルの作成
検証用のテーブルを作成し、現在から 2 時間前を表す値を 1 件 INSERT します。
CREATE SCHEMA IF NOT EXISTS RAW.FRESHNESS_TEST;
CREATE OR REPLACE TABLE RAW.FRESHNESS_TEST.ORDERS_NTZ_JST (
order_id NUMBER,
customer VARCHAR,
amount NUMBER(10, 2),
updated_at TIMESTAMP_NTZ
);
ALTER SESSION SET TIMEZONE = 'Asia/Tokyo';
-- JST で 2 時間前を表す NTZ 値を 1 件 INSERT
INSERT INTO RAW.FRESHNESS_TEST.ORDERS_NTZ_JST VALUES
(1, 'Alice', 100.00, DATEADD('hour', -2, CURRENT_TIMESTAMP()::TIMESTAMP_NTZ));
LAST_ALTEREDの確認
loaded_at_field省略時に参照されるINFORMATION_SCHEMA.TABLES.LAST_ALTEREDの値を確認します。
SELECT
TABLE_CATALOG,
TABLE_SCHEMA,
TABLE_NAME,
LAST_ALTERED,
CONVERT_TIMEZONE('UTC', LAST_ALTERED) AS last_altered_utc,
ROW_COUNT
FROM RAW.INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'FRESHNESS_TEST'
AND TABLE_NAME = 'ORDERS_NTZ_JST';
結果は以下のとおりです。
+---------------+----------------+----------------+-------------------------------+-------------------------------+-----------+
| TABLE_CATALOG | TABLE_SCHEMA | TABLE_NAME | LAST_ALTERED | LAST_ALTERED_UTC | ROW_COUNT |
|---------------+----------------+----------------+-------------------------------+-------------------------------+-----------|
| RAW | FRESHNESS_TEST | ORDERS_NTZ_JST | 2026-05-12 23:39:49.272 -0700 | 2026-05-13 06:39:49.272 +0000 | 1 |
+---------------+----------------+----------------+-------------------------------+-------------------------------+-----------+
LAST_ALTEREDの型はTIMESTAMP_LTZです。検討時のアカウントデフォルトであるAmerica/Los_Angelesで表示されオフセット-0700が付与されています- UTC 換算すると
2026-05-13 06:39:49.272 +0000となり、後述の INSERT 直後の時刻とほぼ一致します
データの確認
追加されたサンプルデータを参照すると以下のようになっています。
SELECT
order_id,
updated_at AS updated_at_raw_ntz,
CONVERT_TIMEZONE('Asia/Tokyo', 'UTC', updated_at) AS updated_at_as_utc,
CURRENT_TIMESTAMP() AS now_session_tz_jst,
CONVERT_TIMEZONE('UTC', CURRENT_TIMESTAMP()) AS now_utc
FROM RAW.FRESHNESS_TEST.ORDERS_NTZ_JST;
+----------+-------------------------+-------------------------+-------------------------------+-------------------------------+
| ORDER_ID | UPDATED_AT_RAW_NTZ | UPDATED_AT_AS_UTC | NOW_SESSION_TZ_JST | NOW_UTC |
|----------+-------------------------+-------------------------+-------------------------------+-------------------------------|
| 1 | 2026-05-13 13:39:47.847 | 2026-05-13 04:39:47.847 | 2026-05-13 15:39:49.347 +0900 | 2026-05-13 06:39:49.347 +0000 |
+----------+-------------------------+-------------------------+-------------------------------+-------------------------------+
updated_atの値は JST ベースの13:39:47ですCONVERT_TIMEZONEを適用すると UTC04:39:47に変換されます- クエリ実行時の UTC は
06:39:49であり、UPDATED_AT_AS_UTC(04:39:47) との差は約 2 時間です(サンプルレコード値は現在から 2 時間前のデータという設定です)
dbt 側で Source を定義
検証用の dbt プロジェクトに Source 定義を作成します。loaded_at_field省略パターンと、比較用の他パターンも追加しています。
version: 2
sources:
# --- 1. loaded_at_field を省略(Snowflake メタデータベース)
- name: freshness_test_a_metadata
database: raw
schema: freshness_test
config:
freshness:
warn_after: { count: 1, period: hour }
error_after: { count: 3, period: hour }
tables:
- name: orders_ntz_jst
# --- 2. NTZ 値を loaded_at_field に指定
- name: freshness_test_b_ntz_raw
database: raw
schema: freshness_test
config:
freshness:
warn_after: { count: 1, period: hour }
error_after: { count: 3, period: hour }
loaded_at_field: updated_at
tables:
- name: orders_ntz_jst
# --- 3. convert_timezone で明示的に UTC 変換
- name: freshness_test_c_ntz_converted
database: raw
schema: freshness_test
config:
freshness:
warn_after: { count: 1, period: hour }
error_after: { count: 3, period: hour }
loaded_at_field: "convert_timezone('Asia/Tokyo', 'UTC', updated_at)"
tables:
- name: orders_ntz_jst
パターン1ではtables配下に対象テーブル名を書くだけで、loaded_at_fieldを指定していません。これにより、メタデータベースでの判定は行われます。
閾値はwarn_after: 1h / error_after: 3hとしています。レコード自体は 2 時間前のデータですが、INSERT 直後のためテーブルのLAST_ALTEREDは直近となり、パターン1では Pass となる想定です。
dbt source freshnessの実行
以下のコマンドで Source freshness を実行します。
dbt source freshness
実行結果は以下のようになりました。
- パターン1:Pass
- loaded_at_field を省略したことで、テーブルの最終変更時刻からの経過時間で判定され、意図通り Pass となった
- パターン2:Pass
- TIMESTAMP_NTZ カラムを直接指定。誤判定
- パターン3:Warn
- TIMESTAMP_NTZ カラムを UTC 変換し適用。意図通り警告となった

Freshness of freshness_test_a_metadata.orders_ntz_jst: pass ← loaded_at_field 省略:意図どおり pass
Freshness of freshness_test_b_ntz_raw.orders_ntz_jst: pass ← NTZ 生値指定:誤判定(後述)
Freshness of freshness_test_c_ntz_converted.orders_ntz_jst: warn ← UTC 変換適用:正しい判定
target/sources.jsonから各パターンの内部値は以下の通りでした。
| パターン | max_loaded_at(UTC) |
snapshotted_at(UTC) |
time_ago_in_s |
判定 |
|---|---|---|---|---|
| 1. メタデータ | 06:39:49.272Z |
06:43:06.429Z |
約 3 分前 | pass |
| 2. NTZ 値 | 13:39:47.847Z |
06:43:06.510Z |
7 時間後の未来扱い | pass(誤判定) |
| 3. NTZ 値を convert | 04:39:47.847Z |
06:43:06.975Z |
約 2 時間前 | warn |
まとめ
パターン1として、判定カラムを省略した場合、以下の特徴で動作することが確認できました。
INFORMATION_SCHEMA.TABLES.LAST_ALTEREDをmax_loaded_atとして利用- 戻り型が
TIMESTAMP_LTZのため、ソースカラム側のタイムゾーン設定や型に関係なく判定可能 - 鮮度判定用のカラムをテーブルに用意する必要がない
- 計測対象は「テーブルへの最終 DML/DDL 時刻」であるため、業務的な意味でのレコードのタイムスタンプとは異なるので注意が必要
カラム単位の精密な鮮度判定までは不要だが、テーブルが定期的に更新されているかは検知したいというユースケースでは、loaded_at_field省略はシンプル選択肢になると思います。
パターン2では誤判定が発生していました。dbt は NTZ をそのまま UTC として解釈するため、パターン3のように UTC への変換が必要です。
より具体的には、以下の通り誤判定となっていました。
- 格納値:
2026-05-13 13:39:47(NTZ、実態は JST の値) - dbt は NTZ をそのまま UTC として解釈し
2026-05-13 13:39:47 UTCとみなす - テスト実行時の UTC は
06:43:06 - 計算結果:
06:43:06 - 13:39:47 = −7 時間となり誤判定
この点は以下でも検証しているため、あわせてご参照ください。
さいごに
dbt の Source freshness でloaded_at_fieldを省略してもテーブル鮮度テストが行えることを Snowflake 上で確認してみました。
こちらの内容がどなたかの参考になれば幸いです。








