dbt

dbtのmacro dispatch(adapter.dispatch)徹底理解 — アダプター別マクロ切替の実務と試験対策

2026-04-19
NicheeLab編集部

同じロジックでも、アダプターごとに最適なSQLや関数は異なります。dbtのmacro dispatchは、実行時のアダプターに応じたマクロ実装へ自動ルーティングする仕組みです。

本記事では、命名規則(default__ / snowflake__ など)と検索順序(dispatch設定)の基本から、実務での上書き・検証・アンチパターンまでを、試験で問われやすい観点とともに整理します。

macro dispatchの基本

adapter.dispatchは、ある“ラッパー”マクロから、実行中のアダプターに最適化された“実装”マクロへ処理を委譲します。呼び出し側は1つのマクロ名に統一しつつ、裏側でsnowflake__...やbigquery__...、default__...といった実装を切り替えます。

既定の検索順序は、プロジェクト設定のdispatchに従います。未設定の場合は一般に「自プロジェクト」→「ラッパーの属するパッケージ」の順で探索し、まずアダプター名接頭辞付きマクロ(例: snowflake__macro)を探し、見つからなければdefault__macroにフォールバックします。

  • ラッパー側: adapter.dispatch('macro名', 'パッケージ名') を呼ぶ
  • 実装側: パッケージ内に snowflake__macro名 / bigquery__macro名 / default__macro名 を定義
  • プロジェクトからの上書き: dispatch.search_order の先頭にプロジェクト名を置く
手段特徴代表的な適用例
adapter.dispatchアダプター別に最適実装を自動選択パッケージのクロスアダプター対応、共通マクロの配布
default__実装アダプター別実装が無いときの安全な後方互換ANSI SQLで書ける汎用ロジック
プロジェクト上書き(dispatch)外部パッケージ実装をローカルで差し替え可能一時的なバグ回避、方言チューニング

最小構成のラッパーと実装

{% macro my_pkg.my_macro(col) %}\n  {%- set impl = adapter.dispatch('my_macro', 'my_pkg') -%}\n  {{ impl(col) }}\n{% endmacro %}\n\n{# 実装: 同一パッケージ(my_pkg)内 #}\n{% macro my_pkg.default__my_macro(col) %}\n  upper({{ col }})  {# どのDWHでも動く汎用例 #}\n{% endmacro %}\n\n{% macro my_pkg.snowflake__my_macro(col) %}\n  upper({{ col }})::string  {# Snowflake最適化の一例 #}\n{% endmacro %}

命名規則と検索順序(dispatch)

実装マクロ名は「接頭辞__本来のマクロ名」という形です。接頭辞はアダプター種別名(snowflake, bigquery, redshift, postgres, spark, databricks など)または default を使用します。ラッパーは接頭辞を付けず、adapter.dispatchで“マクロ名”と“パッケージ名”を指定します。

検索順序は dbt_project.yml の dispatch で制御します。macro_namespace にラッパーの属するパッケージ名を指定し、search_order でパッケージ探索の優先順位を決めます。多くのプロジェクトでは、まず自分のプロジェクトを先頭に置いて上書き可能にし、次に元のパッケージを指定します。

  • 実装命名: snowflake__macro, bigquery__macro, default__macro
  • ラッパーはパッケージ名空間に属し、dispatch先の“マクロ名”は同名
  • search_orderはプロジェクト→パッケージの順が定番

adapter.dispatchの探索フロー

call: adapter.dispatch(m, p)search_order 配列例: [my_project, my_pkg]pkg.{adapter}__m 存在?Yes→使用 / No→次へpkg.default__m 存在?Yes→使用 / No→次のpackageどこにも無ければエラー各パッケージで adapter__m → default__m の順に検索、見つからなければ次のパッケージへ

dispatchの設定例(dbt_project.yml)

dispatch:\n  - macro_namespace: my_pkg\n    search_order: ['my_project', 'my_pkg']\n\n# ラッパー my_pkg.my_macro からの探索順:\n# 1) my_project.snowflake__my_macro -> 無ければ my_project.default__my_macro\n# 2) my_pkg.snowflake__my_macro     -> 無ければ my_pkg.default__my_macro

プロジェクトでの上書き戦略

外部パッケージのマクロ動作を一時的に調整したい場合、search_orderの先頭に自プロジェクトを置き、同名の実装マクロを定義して上書きします。パッケージ本体をフォークせずに差分だけ保守できるのが利点です。

実装を上書きする場合も名前は変えません。例: 元パッケージが my_pkg.snowflake__my_macro を持つなら、プロジェクト側でも snowflake__my_macro を自プロジェクトの名前空間で定義します。

  • search_order 先頭は自プロジェクト
  • 本家と同じ実装名(snowflake__..., default__...)で定義
  • 将来のパッケージ更新で差分が解消されたら削除して戻せる

プロジェクトでの上書き例

{# packages.yml #}\npackages:\n  - package: my_org\n    version: 1.0.0\n\n{# dbt_project.yml #}\ndispatch:\n  - macro_namespace: my_org\n    search_order: ['my_project', 'my_org']\n\n{# プロジェクト側(上書き): models/macros/snowflake__my_macro.sql #}\n{% macro my_project.snowflake__my_macro(col) %}\n  to_varchar(upper({{ col }}))  {# Snowflake向けの細かな上書き #}\n{% endmacro %}

選択結果のテストとデバッグ

実装がどれに解決されたかを把握するには、run-operationでログを出す簡易マクロを用意します。adapter.type や target.type を併記すると、実行環境との対応が確認しやすくなります。

CIでは複数プロファイル(例: snowflake, bigquery)でサンプルモデルを実行し、同じラッパーが環境ごとに異なる実装へ正しく切り替わるかを検証します。

  • run-operationでログ出力して実装名を確認
  • 複数アダプターのスモークテストを用意
  • 予期せぬdefaultフォールバックを検知する

どの実装が選ばれたかをログ出力

{% macro debug_my_macro_resolution() %}\n  {# ラッパー名とネームスペース #}\n  {% set m = adapter.dispatch('my_macro', 'my_pkg') %}\n  {% do log('adapter: ' ~ target.type, info=True) %}\n  {# マクロオブジェクト自体は文字列化で名前を含むことが多い #}\n  {% do log('resolved macro: ' ~ (m|string), info=True) %}\n{% endmacro %}\n\n# 実行例: dbt run-operation debug_my_macro_resolution

アンチパターンと落とし穴

if分岐でadapter.typeを判定してSQLを書き分けるのは、拡張性・保守性が低く、パッケージ配布にも不向きです。dispatchなら新しいアダプターが増えても実装マクロを足すだけで済みます。

ラッパーのネームスペースとdispatchに渡すパッケージ名が一致していないと、意図した実装が見つからずにdefaultへ落ちる、あるいは解決不能でエラーになります。

  • NG: モデル内で if target.type == 'snowflake' のような分岐乱立
  • NG: ラッパーmy_pkgに対して別パッケージ名をdispatchに渡す
  • 注意: 実装名の綴り(snowflake/bigquery/databricks 等)ミス

分岐ベタ書きからdispatchへのリファクタ

{# 悪い例 #}\n{% macro my_macro(col) %}\n  {% if target.type == 'snowflake' %}\n    {{ return('upper(' ~ col ~ ')::string') }}\n  {% elif target.type == 'bigquery' %}\n    {{ return('cast(upper(' ~ col ~ ') as string)') }}\n  {% else %}\n    {{ return('upper(' ~ col ~ ')') }}\n  {% endif %}\n{% endmacro %}\n\n{# 良い例: ラッパー + 実装 #}\n{% macro my_pkg.my_macro(col) %}\n  {% set impl = adapter.dispatch('my_macro', 'my_pkg') %}\n  {{ impl(col) }}\n{% endmacro %}\n\n{% macro my_pkg.default__my_macro(col) %} upper({{ col }}) {% endmacro %}\n{% macro my_pkg.bigquery__my_macro(col) %} cast(upper({{ col }}) as string) {% endmacro %}\n{% macro my_pkg.snowflake__my_macro(col) %} upper({{ col }})::string {% endmacro %}

複数アダプター対応の実務パターン

Spark系(オープンソースSpark)とDatabricksはSQL方言が近いケースが多い一方、実際の関数名やデータ型、DDLの挙動が異なることがあります。必要に応じて spark__ と databricks__ の両方を用意し、片方をdefaultに寄せるのではなく、それぞれ最小差分で最適化するのが安全です。

外部パッケージを作る場合は、まずdefault__を堅牢にし、主要アダプター(snowflake/bigquery/redshift/postgres/spark/databricks)に対する最小実装を段階的に追加します。プロジェクト側のsearch_orderで一時的なホットフィックスも可能にしておくと運用が楽になります。

  • SparkとDatabricksは別接頭辞(spark__/databricks__)
  • default__は必ず実装して後方互換を確保
  • 差分は実装マクロ単位で閉じ込め、呼び出し側を汚さない

複数アダプター最小実装の雛形

{% macro util_pkg.normalize_text(col) %}\n  {% set impl = adapter.dispatch('normalize_text', 'util_pkg') %}\n  {{ impl(col) }}\n{% endmacro %}\n\n{% macro util_pkg.default__normalize_text(col) %}\n  trim(lower({{ col }}))\n{% endmacro %}\n\n{% macro util_pkg.bigquery__normalize_text(col) %}\n  trim(lower(cast({{ col }} as string)))\n{% endmacro %}\n\n{% macro util_pkg.databricks__normalize_text(col) %}\n  trim(lower({{ col }}))  {# 必要に応じて正規化関数を追加 #}\n{% endmacro %}

問題で確認

Analytics Engineer

問題 1

次の設定と実装がある。ターゲットは Snowflake。呼び出し {{ my_pkg.my_macro('name') }} 実行時に選ばれる実装はどれか。

  1. my_project.snowflake__my_macro
  2. my_project.default__my_macro
  3. my_pkg.snowflake__my_macro
  4. my_pkg.default__my_macro

正解: A

dispatch設定が macro_namespace: my_pkg, search_order: ['my_project', 'my_pkg'] の場合、探索は 1) my_project.snowflake__my_macro → 無ければ my_project.default__my_macro → 2) my_pkg.snowflake__my_macro → 無ければ my_pkg.default__my_macro の順。Snowflakeターゲットで先頭の my_project.snowflake__my_macro が存在すればそれが選択される。

よくある質問

dispatchを設定しない場合の探索順序は?

一般的には自プロジェクト→ラッパーの属するパッケージの順に探索され、まずアダプター接頭辞付き(例: snowflake__)、無ければdefault__が評価されます。確実な制御が必要ならdbt_project.ymlでdispatchを明示しましょう。

ラッパーと実装は同じパッケージに置く必要がありますか?

ラッパーの属する“ネームスペース”に対して実装(default__/snowflake__など)を定義します。外部パッケージを配布する場合は、そのパッケージ内に実装を揃え、プロジェクト上書きはsearch_orderで自プロジェクトを先頭にするのが通例です。

SparkとDatabricksのどちらの実装を用意すべき?

互換性が高い部分もありますが差異もあるため、両方の接頭辞(spark__/databricks__)を別々に用意するのが安全です。片方のみで代替する場合はCIで双方のターゲットを実行し、意図せぬdefaultフォールバックや構文差異がないか検証してください。

この記事で学んだ内容を問題で確認しましょう

16,000問以上の問題で実力チェック

無料で問題を解いてみる
この記事の著者

NicheeLab編集部

データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。


関連記事
dbt

dbt Model の基礎: SQL で定義する変換の最小単位

Analytics Engineer 向けに、dbt Model の定義、マテリアライゼーション、依存関係、インクリメン...

dbt

dbt Analytics Engineer 試験ガイド: 出題範囲・配点・申込の実務視点

dbt Analytics Engineer 認定の出題範囲、配点の考え方、申込から受験までの流れを、公式ドキュメントの...

dbt

dbt Cloud と dbt Core の違いと選び方:Analytics Engineer 試験に効く要点

dbt Cloud と dbt Core の機能差を、実務と資格対策の両面から整理。スケジューリング、IDE、RBAC、...

dbt

dbt プロジェクト構造ガイド: models / seeds / macros の実務レイアウト

Analytics Engineer 向けに、dbt プロジェクトのディレクトリ構造と命名規約、dbt_project....

dbt

dbt_project.yml の読み方:主要設定と命名を最短で掴む

dbt_project.yml の必須キー、命名解決(database.schema.identifier)、設定優先度...

dbtの記事一覧 (100件)
© 2026 NicheeLab All rights reserved.