ref() はdbtプロジェクトのDAGを形成し、モデルの実体名解決とビルド順序を自動化します。Analytics Engineer試験でも頻出の基本です。
本稿では、ref()の解決タイミング、マテリアライゼーションとの関係、クロスパッケージ参照、seedやsnapshotとの連携、アンチパターン、トラブルシューティングまでを一気通貫で確認します。
ref() はコンパイル時に、与えたモデル名を環境に応じた実体名(データベース.スキーマ.テーブル/ビュー)へ解決し、さらに依存関係グラフにエッジを追加します。これにより dbt build は上流から下流へ正しい順番で実行され、トランジティブな依存も自動で考慮されます。
実体名はtarget(プロファイルとターゲット)に基づき、アダプタ(Snowflake、BigQuery、Databricks SQLなど)の規則に沿って適切にクオート・命名されます。物理名をハードコードしないため、開発/本番でスキーマが異なってもSQLを共通化できます。
ref() によるDAGのイメージ
[seed_orders] ---> [stg_orders] ----> \
^ \
| > [fct_orders]
[stg_payments] -------/ /
[dim_customers] ----------------------->/基本例: fct_orders.sql
with orders as (
select * from {{ ref('stg_orders') }}
),
payments as (
select * from {{ ref('stg_payments') }}
)
select
o.order_id,
o.customer_id,
sum(p.amount) as revenue
from orders o
left join payments p on p.order_id = o.order_id
group by 1,2;ref() はコンパイル段階で解決されます。tableやview、incrementalなどのマテリアライゼーションでは、ref() は対応するリレーション名に展開されます。一方、ephemeral モデルを参照した場合、dbtはそのSQLを下流モデル内のCTEとしてインライン展開します(物理テーブルは作られません)。
この動作により、上流SQLの再利用と最適化を両立できます。ただし、ephemeralの多用はCTEが肥大化し、コンパイル後SQLが複雑になる可能性があるため、クエリプランや実行時間を観察しつつ、テーブル化やインクリメンタル化の検討が必要です。
ephemeralの例: インライン展開される補助モデル
-- models/utils_joined.sql
{{ config(materialized='ephemeral') }}
select *
from {{ ref('stg_orders') }} o
join {{ ref('stg_payments') }} p on p.order_id = o.order_id;
-- models/fct_orders.sql(上記を参照)
select order_id, customer_id, sum(amount) as revenue
from {{ ref('utils_joined') }}
group by 1,2;パッケージ間でモデルを使い回す場合、packages.yml に依存関係を宣言し、ref('package_name', 'model_name') で明示的に参照します。これにより名前衝突を避け、どのパッケージのモデルかが一目で分かります。
同名モデルが存在する可能性がある大規模モノレポでは、クロスパッケージ参照を癖にするのが安全です。CIでは依存解決とキャッシュが正しく働くため、ビルドが安定します。
クロスパッケージrefの設定例
# packages.yml
packages:
- package: org/payments_pkg
version: 0.6.1
-- models/fct_orders.sql
select *
from {{ ref('core_pkg', 'stg_orders') }} o
left join {{ ref('payments_pkg', 'stg_payments') }} p
on p.order_id = o.order_id;ref() はモデルだけでなく seed や snapshot にも使えます。seedはCSVから作られる静的テーブル、snapshotはSCD管理用の履歴テーブルです。いずれもref()で参照することでDAGに組み込み、ビルド順序と命名解決の恩恵を受けられます。
生データの外部テーブルを参照する場合は source() を使います。sourceはデータウェアハウスに既存の生テーブルを宣言的に登録し、ドキュメント化と依存管理を可能にする仕組みです。
singularテストの例: revenueがNULLでないことを検証
-- tests/test_revenue_not_null.sql
select 1
from {{ ref('fct_orders') }}
where revenue is null
limit 1;スキーマやテーブル名のハードコードは最も多いアンチパターンです。環境差分に弱く、DAGにも載らないため順序保証が失われます。常にref() / source()を優先しましょう。
ephemeralの多段ネストはコンパイル後SQLを巨大化させます。クエリ計画の確認、重要中間結果のテーブル化(materialized='table' や incremental化)を検討してください。
悪例と良例
-- 悪例: 物理名のハードコード(環境移行で破綻)
select * from PROD_ANALYTICS.core.stg_orders;
-- 良例: ref()で環境依存を解消し、DAGに参加
select * from {{ ref('stg_orders') }};dbt build -s fct_orders を実行すると、ref() で到達可能な上流モデルが自動で先に実行されます。変更影響範囲だけを実行したい場合は state 比較のセレクタを使います。
典型的なエラーは Model not found for ref。モデル名のタイプミス、対象モデルがdisabled、パッケージ未導入、選択子で除外されている、などを確認します。
| 対象 | 依存グラフ登録 | 環境可搬性 | 代表的な用途 |
|---|---|---|---|
| ref() | あり(上流→下流の順序保証) | 高い(targetに応じて解決) | モデル・seed・snapshot の参照 |
| source() | あり(ソース→モデル) | 中(宣言は環境依存しないが実体は外部) | 生データの既存テーブル参照 |
| 直接DB参照 | なし | 低い(環境や命名に固定) | 暫定調査や一時的な手動クエリ |
選択子と実行例
# 依存込みで特定モデルのみを実行
$ dbt build -s fct_orders
# 変更のあった下流を実行(state比較)
$ dbt build -s state:modified+ --state path/to/artifacts
# 参照解決の確認
$ dbt list -s fct_orders --output nameAnalytics Engineer
問題 1
モデル fct_orders が {{ ref('stg_orders') }} と {{ ref('stg_payments') }} を参照している。dbt build -s fct_orders を実行したときの挙動として正しいのはどれか。
正解: A
ref() はDAGに依存エッジを追加し、上流から順にビルドします。実体名の解決はtargetに依存するため、環境ごとに適切なスキーマ・データベースに展開されます。マテリアライゼーション設定は無視されません。
ref() と source() はどう使い分けるべきですか?
ref() はdbtが管理するモデル・seed・snapshotを参照する際に使います。source() はデータウェアハウス上の既存の生テーブルを宣言して参照するために使います。外部データの取り込み起点はsource()、その後の変換はref()でつなぐのが基本です。
モデル名を変更したいが、参照を壊したくありません。どうすればよいですか?
物理テーブル名だけ変えたい場合はモデルのaliasを使います(モデル名は据え置き)。これによりref('旧モデル名')は引き続き解決され、物理名だけが変更されます。論理モデル名自体を変える場合は、移行期間を設けて旧モデルを残すか、下流の参照先を一斉に置換してDAGを更新します。
クロスパッケージ参照でエラーになります。何を確認すべきですか?
packages.ymlで該当パッケージが導入されているか、バージョンが正しいかを確認し、ref('package_name', 'model_name') の2引数形式で記述しているかを見直してください。さらに、対象モデルがdisabledでないこと、選択子で除外されていないことも確認します。
NicheeLab編集部
データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。
dbt Model の基礎: SQL で定義する変換の最小単位
Analytics Engineer 向けに、dbt Model の定義、マテリアライゼーション、依存関係、インクリメン...
dbt Analytics Engineer 試験ガイド: 出題範囲・配点・申込の実務視点
dbt Analytics Engineer 認定の出題範囲、配点の考え方、申込から受験までの流れを、公式ドキュメントの...
dbt Cloud と dbt Core の違いと選び方:Analytics Engineer 試験に効く要点
dbt Cloud と dbt Core の機能差を、実務と資格対策の両面から整理。スケジューリング、IDE、RBAC、...
dbt プロジェクト構造ガイド: models / seeds / macros の実務レイアウト
Analytics Engineer 向けに、dbt プロジェクトのディレクトリ構造と命名規約、dbt_project....
dbt_project.yml の読み方:主要設定と命名を最短で掴む
dbt_project.yml の必須キー、命名解決(database.schema.identifier)、設定優先度...