
dbt platformでジョブをエラー終了させずに祝日のみ更新する方法を実装してみた
かわばたです。
前回の記事では、dbtで祝日マスターテーブルを作成し、マクロを使って祝日のみジョブを実行する方法を紹介しました。
祝日の日だけ実行し、それ以外の日付はジョブをエラーとして終了させるものでした。
課題感として、エラー検知を運用でカバーする必要があります。
今回は別の手法でエラー終了を前提とせずに、マクロを使ったモデル内での条件分岐を試していきます。
【前回の記事】
【公式ドキュメント】
Jinja and macros
incremental models
対象読者
- dbtモデル内で祝日・祝日でない日による条件分岐を実装したい方
- Jinjaの
if文を使った動的SQLを学びたい方
検証環境
- dbt platform Enterprise版
- Latestバージョン
- Snowflakeトライアルアカウント Enterprise版
今回のゴール
前回は「祝日でなければジョブを中断する」という方法でした。今回は以下を実現します。
- モデル内での条件分岐: 祝日ならseedから更新、祝日でない日なら既存データを維持
【処理フロー】
祝日の場合:
dbt run → is_today_holiday() = true → seedから最新データを取得 → テーブル更新
祝日ではない場合:
dbt run → is_today_holiday() = false → 既存テーブル相当の内容を返す → 実質的に変更なし(dbtの実行自体は行う)
is_today_holiday マクロの実装
マクロの概要
is_today_holiday() は true/falseを返すマクロです。Jinjaの if 文と組み合わせて、モデル内で条件分岐を実現できます。
マクロのコード(holiday_job_control.sql)
{#
============================================================================
is_today_holiday マクロ
============================================================================
概要:
今日が祝日かどうかを判定し、true/false を返す
Jinja の if 文で分岐処理に使用可能
使用方法:
{% if is_today_holiday() %}
-- 祝日の処理
{% else %}
-- 祝日でない日の処理(または何もしない)
{% endif %}
戻り値:
祝日の場合: true
祝日でない日の場合: false
#}
{% macro is_today_holiday() %}
{% set is_holiday_query %}
select count(*) as cnt
from {{ target.database }}.DBT_TKAWABATA.JAPANESE_HOLIDAYS
where holiday_date = DATE(CONVERT_TIMEZONE('UTC', 'Asia/Tokyo', CURRENT_TIMESTAMP()))
{% endset %}
{% if execute %}
{% set result = run_query(is_holiday_query) %}
{% set holiday_count = result.columns[0].values()[0] %}
{{ return(holiday_count > 0) }}
{% else %}
{{ return(false) }}
{% endif %}
{% endmacro %}
ポイント解説
| 要素 | 説明 |
|---|---|
run_query() |
dbt実行時にSnowflakeへクエリを発行し、結果を取得 |
{% if execute %} |
compile時とrun時を区別。compile時はクエリを実行しない |
{{ return(true/false) }} |
マクロから値を返す |
incrementalモデルでの条件分岐(incremental_dim_holidays)
is_today_holiday() マクロと incremental マテリアライゼーションを組み合わせて、祝日のみ更新するモデルを実装します。
実装コード
{#
============================================================================
incremental_dim_holidays - 日本の祝日マスターテーブル
============================================================================
概要:
日本の祝日データを管理するディメンションテーブル
動作:
- 初回実行/フルリフレッシュ: seedから全データをロード
- 増分実行 + 祝日: seed全件をソースとしてMERGEし、既存行を更新・新規行を追加
- 増分実行 + 祝日でない日: where 1=0 により何も追加せず既存データを維持
- この例では差分抽出は行わず、incremental materialization を“条件付き更新制御”のために使っています
#}
{{ config(
materialized='incremental',
unique_key='holiday_date'
) }}
with source_data as (
select
holiday_date,
holiday_name,
holiday_type,
extract(year from holiday_date) as holiday_year,
extract(month from holiday_date) as holiday_month,
dayname(holiday_date) as day_of_week
from {{ ref('japanese_holidays') }}
)
select * from source_data
{% if is_incremental() and not is_today_holiday() %}
where 1 = 0 -- 祝日でない日の増分実行時は何も追加しない
{% endif %}
ポイント解説
| 要素 | 説明 |
|---|---|
materialized='incremental' |
増分更新モード。既存テーブルに対してMERGE/INSERTを実行 |
unique_key='holiday_date' |
マージ時のキー。同じ日付があれば更新、なければ追加 |
is_incremental() |
dbt組み込み関数。増分実行時のみ true を返す |
where 1 = 0 |
常にFALSEとなる条件。結果として0行が返される |
動作パターン
| 状況 | 動作 |
|---|---|
| 初回実行(祝日でない日/祝日問わず) | 全データをロード |
| フルリフレッシュ | 全データをロード |
| 増分実行 + 祝日 | seed全件をソースとしてMERGEし、既存行を更新・新規行を追加 |
| 増分実行 + 祝日でない日 | where 1=0 で何も追加しない |
Jinjaのif文で条件分岐(dim_holidays.sql)
このパターンは、平日分岐で既存テーブルを参照するため、対象テーブルがすでに存在していることが前提です。初回実行や削除後の再作成時には別途考慮が必要です。
実装コード
{#
============================================================================
dim_holidays - 日本の祝日マスターテーブル
============================================================================
概要:
日本の祝日データを管理するディメンションテーブル
動作:
- 祝日の場合: seedデータから最新の祝日マスターを再構築
- 祝日でない日の場合: 既存テーブルと同等の内容で再作成し、結果として実質的なデータ変更を発生させない
#}
-- depends_on: {{ ref('japanese_holidays') }}
{{ config(
materialized='table'
) }}
{% if is_today_holiday() %}
{# === 祝日の場合: seedから最新データを取得して更新 === #}
{{ log("Today is a holiday! Updating dim_holidays table.", info=True) }}
select
holiday_date,
holiday_name,
holiday_type,
extract(year from holiday_date) as holiday_year,
extract(month from holiday_date) as holiday_month,
dayname(holiday_date) as day_of_week
from {{ ref('japanese_holidays') }}
{% else %}
{# === 祝日でない日の場合: 既存テーブルと同等の内容を返し、実質的な変更を発生させない === #}
{{ log("Today is not a holiday. Keeping existing dim_holidays data.", info=True) }}
select
holiday_date,
holiday_name,
holiday_type,
holiday_year,
holiday_month,
day_of_week
from {{ target.database }}.DBT_TKAWABATA.dim_holidays
{% endif %}
-- depends_on の必要性
動作確認
incrementalモデルでの条件分岐
事前準備
祝日マスタの元となるcsvにテストデータを入れます。

dbt seedでseedデータをSnowflakeにロードします。

初回実行を行います。
dbt run --select incremental_dim_holidays --full-refresh

祝日ではない日の場合(例: 2026-03-16)
下記のように増分対象となるデータを追加し、dbt seedを実行します。

データが追加されていることを確認できました。

dbt run --select incremental_dim_holidays
上記を実行し、増分実行時のロジックが適用されていることを確認します。

想定通りSnowflakeの対象テーブルには反映されていませんでした。
祝日の場合
祝日マスタに2026-03-16を加えて実行していきます。

データが追加されていることを確認できました。

dbt run --select incremental_dim_holidays
上記を実行し、2回目以降のロジックが適用されていることを確認します。(祝日なのでレコードが更新される)

Jinjaのif文での条件分岐
※検証日が2026-03-15のため、incrementalモデルでの条件分岐の検証日とズレています。
祝日ではない日の場合(例: 2026-03-15)
dim_holidaysテーブルに格納されているデータ

祝日マスタの元となるcsvにテストデータを入れます。

dbt seedでseedデータをSnowflakeにロードします。

dbt runを実行します。Today is not a holiday. Keeping existing dim_holidays dataと想定通りのログが出力されました。

Snowflake の対象テーブルには反映されていませんでした。

祝日の場合
祝日マスタに2026-03-15を加えて実行していきます。

dbt seedでseedデータをSnowflakeにロードします。

dbt runを実行します。Today is a holiday! Updating dim_holidays tableと想定通りのログが出力されました。

Snowflake の更新先に想定通り反映されていました。

運用時のポイント
Snowflake挙動
Snowflake の CREATE OR REPLACE TABLE 自体は atomic に実行されます。並行クエリは旧版または新版のいずれかを参照します。
ただし、dbt の materialization 実装や依存関係、初回実行時の存在有無には注意が必要です。
初回実行時
初回実行時に対象テーブルが未作成の場合、祝日でない日の分岐では既存テーブルを参照できず失敗する可能性があります。初回は祝日分岐で作成する、または存在確認を入れる実装を検討してください。
ログの確認
モデルの実行ログで、どちらの分岐が実行されたか確認できます。
# 祝日の場合
Today is a holiday! Updating dim_holidays table.
# 祝日ではない日の場合
Today is not a holiday. Keeping existing dim_holidays data.
コスト面の確認
今回はジョブエラーを前提とせず、祝日の場合のみ更新を行い、それ以外の日付は既存のデータのままという構成です。
そのため、実行自体は行われており毎日ジョブが回っていることになり、その分の実行コストが発生する点に注意が必要です。
最後に
今回はマクロを使ったモデル内での条件分岐を検証しました。
ポイントまとめ:
is_today_holiday()でtrue/falseを返し、Jinjのif文で分岐incremental+where 1=0パターンで安全に条件分岐を実現-- depends_on:で条件分岐内のref()の依存関係を明示
前回の「ジョブを中断する」方法と、今回の「モデル内で分岐する」方法ではそれぞれの課題がトレードオフの関係にあるため、ご自身の組織で運用しやすい方を選択ください。
個人的にはincrementalモデルでの管理もやりやすいなと感じました。
この記事が何かの参考になれば幸いです!







