dbt compile は「実行しない dbt」。Jinja とマクロを評価し、依存関係を解決して、実行可能なSQLに展開するが、テーブル・ビューを作らない。この境界を正しく理解すると、レビュー効率と失敗率が大きく変わる。
本稿では、公式ドキュメントの挙動に沿って compile の内部で何が起きているかを分解し、Analytics Engineer 試験で狙われやすいポイントと、現場で役立つ検証ワークフローを整理する。
dbt compile は、プロジェクト内のモデル・スナップショット・テストなどのノードを読み込み、Jinja/マクロを評価し、ref/source/config などの依存を解決した上で、最終的に実行可能なSQLを target/compiled 配下へ出力するコマンドである。データベースには一切DML/DDLを発行しないため、安全に「最終SQL」を確認できる。
run や build の前段確認、レビュー用の差分検証、CIでの構文崩れ検知などに適する。特に ephemeral モデルのインライン化や adapter 固有マクロの展開結果を目視確認できる点は大きい。
dbt の処理段階と compile の位置づけ
最小モデルの before/after(概念)
-- models/orders.sql(テンプレート)
with src as (
select * from {{ source('raw', 'orders') }}
)
select * from src where order_date >= '{{ var('from_date', '2024-01-01') }}'
-- target/compiled/<project>/models/orders.sql(展開後の一例)
with src as (
select * from RAW.SOURCE_ORDERS -- adapterや命名規則でFQNに解決
)
select * from src where order_date >= '2024-01-01'
compile は、選択対象ノードを特定し、各リソースのSQLファイルをJinja環境でレンダリングする。マクロ呼び出しや adapter.dispatch によるアダプタ固有実装が展開され、ref/source は実際に作成(または作成予定)のリレーション名に置換される。ephemeral は上流に物理化されないため、参照側のクエリにCTEとして埋め込まれる。
tests は compile 段階で「失敗行を返すSELECT」などの実行用SQLに変換される。ただし compile 自体はそれを実行しない点が重要。
| コマンド | 主な目的 | 生成物(ターゲット) | 実行(DDL/DML) |
|---|---|---|---|
| dbt parse | 構文解析とDAG生成 | manifest.json 等 | なし |
| dbt compile | Jinja/マクロ評価とSQL展開 | target/compiled, manifest.json | なし |
| dbt run | モデルSQLの実行 | target/run, run_results.json | あり |
| dbt build | run+test(+snapshot)の一括実行 | target/run ほか | あり |
| dbt test | テストSQLの実行 | target/run, run_results.json | あり |
ephemeral のインライン化イメージ
-- models/_dim_ephemeral.sql
-- {{ config(materialized='ephemeral') }}
select id, lower(email) as email_norm from {{ ref('stg_users') }}
-- models/dim_users.sql
select u.*, e.email_norm
from {{ ref('stg_users') }} u
left join {{ ref('_dim_ephemeral') }} e on u.id = e.id
-- target/compiled/.../dim_users.sql(概念)
select u.*, e.email_norm
from PROD.ANALYTICS.STG_USERS u
left join (
select id, lower(email) as email_norm from PROD.ANALYTICS.STG_USERS
) e on u.id = e.id
compile 実行後、展開済みSQLは target/compiled/<project>/... に書き出される。これが「実行されるはずのSQL」の静的スナップショットとなる。manifest.json はDAGとメタデータ(リレーション名、依存、コンフィグ)を持ち、ツール連携や差分解析に有用である。
run や test と異なり、target/run は通常 compile では生成されない。実行ログや run_results.json を期待してはいけない。
典型的な出力のツリー
$ dbt compile --select +models/orders.sql
target/
compiled/
my_project/
models/
staging/
stg_users.sql
marts/
orders.sql # 展開後の最終SQL
manifest.json # DAGとメタデータ
logs/
dbt.log
compile は Jinja/マクロを実際に評価するため、変数 var()、環境変数 env_var()、config() による分岐、adapter.dispatch による方言分岐が反映される。一方で、SQL自体は実行されないため、実データに依存する条件分岐の最終的な効果までは検証できない。
is_incremental() はインクリメンタルモデル内の分岐に用いられるが、コンパイル時点のコンテキストや接続可否、既存リレーションの存在判定に依存する。compile はDDL/DMLを打たないが、マクロ評価のためにアダプタ情報を参照することがある。最終動作保証には run/build での確認が必要。
インクリメンタル分岐の一例(compile時に展開)
-- models/fct_events.sql
{{ config(materialized='incremental', unique_key='id') }}
select * from {{ ref('stg_events') }}
{% if is_incremental() %}
where _ingest_ts > (select max(_ingest_ts) from {{ this }})
{% endif %}
-- 注意: compile は上記を展開するが、既存テーブル有無などの前提は実行しない限り確定しない
compile の選択挙動は run と同様で、--select と --exclude、+(親子)演算子、タグ、リソース種別(model:、test: など)が使える。これにより、レビュー対象を最小限に絞り、差分箇所のみの展開を確認できる。
ephemeral 上流は参照先モデルにインライン化されるため、見落としを避けるには +演算子やパスベース選択で周辺ノードもコンパイルしておくのが安全。
選択例
$ dbt compile --select path:models/marts/
$ dbt compile --select model:orders+
$ dbt compile --select state:modified --state ./target
未定義の変数、未解決の ref/source、マクロのディスパッチ不一致は compile で発覚しやすい。まず dbt parse で構文段階のエラーを早期に潰し、その後 compile で展開結果を確認するワークフローが堅実だ。
Analytics Engineer 試験では、compile が「実行しない」「ephemeral をインライン化する」「ref/source をFQNへ解決する」「target/compiled と manifest.json を生成する」といった特性を選択肢で見極める問題が頻出する。実務では PR で compiled SQL を添付し、レビュー効率を高めるのが定石。
デバッグの基本
$ dbt clean && dbt deps
$ dbt parse # 構文とDAG
$ dbt compile -s +model:orders --vars '{from_date: 2024-01-01}'
$ tail -n 100 logs/dbt.log # マクロや未解決参照の痕跡を確認
Analytics Engineer
問題 1
dbt compile について正しい説明はどれか。最も適切なものを選べ。
正解: A
dbt compile はJinja/マクロ評価と依存解決を行い、展開済みSQLを target/compiled に書き出すが、DDL/DMLの実行はしない。Bは parse の説明に近く誤り、Cは test の挙動、Dは ephemeral の理解が誤りで、ephemeral はコンパイル時に参照側へインライン化される。
compile はデータベース接続を必要とするか?
ユーザーSQLの実行は行わないが、マクロ評価やアダプタ情報の参照で接続が初期化される場合がある。接続不要と仮定せず、適切なプロファイル設定を用意しておくのが安全。
target/compiled と target/run の違いは?
target/compiled は展開済みSQL(実行前の静的成果物)。target/run は run/test/build 実行時に使用されたSQLや一部生成物を格納する。compile では通常、target/run は作成されない。
compile の結果だけでインクリメンタル分岐は検証できる?
分岐の展開は確認できるが、既存テーブルの有無や最大タイムスタンプなど実データに依存する条件の最終挙動は compile だけでは保証できない。最終確認は run/build で行う。
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)、設定優先度...