なぜdbtなのか — SQLファイルが50個を超えた瞬間
データエンジニアとして働いていると、誰もが一度は遭遇する光景があります。プロジェクト初期はSQLファイルが4〜5個で、まだ全体像を把握できます。しかし半年も経つとフォルダ内のSQLスクリプトは50個を超え、どのスクリプトがどの順序で実行されるのか、誰も確信を持てなくなります。
- カラム名が1つ変わっただけで、下流のダッシュボードが静かに壊れる
- 夜間の1,000万件ロードジョブが失敗し、システム全体が停止する
- 新しいメンバーは、全SQLファイルを開くまでプロジェクトを理解できない
従来、SQLは「孤立したスクリプトの集合」として扱われてきました。dbtはこの前提を覆します。SQLをソフトウェアプロジェクトの構成要素として扱うのです。各変換(transformation)は明確な目的を持ち、依存関係はコードとして定義されます。
本記事では、dbt Core(無料、Apache 2.0)とDuckDB(MITライセンス)をローカルにセットアップし、モデル・ソース・テスト・ドキュメント生成までを一通り確認します。エンタープライズではSnowflake、BigQuery、Redshift、Databricksと組み合わせて使われるツールですが、ローカルでも同じ流れで練習できます。根拠資料は Getting Started with dbt を参照してください。

インストールから最初のモデル実行まで
1) 仮想環境とdbtのインストール
プロジェクトを分離するため、まずPython仮想環境を作成します。本記事はPython 3.13で検証しています。
# プロジェクトフォルダ作成と仮想環境の有効化
mkdir dbt-demo
cd dbt-demo
python3 -m venv .venv
.\.venv\Scripts\Activate.ps1
# dbt-coreとduckdbアダプタをインストール(DuckDBは1ファイルで完結)
pip install dbt-duckdb
2) dbtプロジェクトの初期化
dbt init
# プロジェクト名: my_dbt_demo(ハイフン不可、アンダースコアを使用)
# DB選択: 1 (duckdb)
dbt init が完了すると、ホームディレクトリ(C:\Users\<user>\.dbt\)に profiles.yml が生成されます。このファイルがDB接続情報を保持します。DuckDBのファイルパスをプロジェクトフォルダ内に移動しておきましょう。
# ~/.dbt/profiles.yml
my_dbt_demo:
outputs:
dev:
type: duckdb
path: "{{ env_var('USERPROFILE') }}/projects/dbt-demo/data/duckdb.dev"
schema: raw
threads: 1
prod:
type: duckdb
path: "{{ env_var('USERPROFILE') }}/projects/dbt-demo/data/duckdb.prod"
schema: raw
threads: 4
target: dev
3) ソース(source)とモデル(model)の定義
ソースはdbtが作成していない既存の元テーブルです。モデルはdbtが実行するSQLの断片で、新しいテーブルまたはビューを作成します。両者をYAMLで関連付けておけば、テーブル名が変わっても1箇所の修正で済みます。
# models/orders.yml
version: 2
sources:
- name: raw
schema: raw
tables:
- name: orders
-- models/customer_orders_summary.sql
{{ config(materialized='table') }}
with completed_orders as (
select
order_id,
customer_name,
order_date,
quantity,
quantity * unit_price as order_value
from {{ source('raw', 'orders') }}
where lower(order_status) = 'completed'
)
select
customer_name,
count(*) as completed_order_count,
sum(quantity) as total_units_purchased,
round(sum(order_value), 2) as total_revenue,
round(avg(order_value), 2) as average_order_value,
min(order_date) as first_order_date,
max(order_date) as most_recent_order_date
from completed_orders
group by customer_name
4) 実行と確認
dbt run
# 1 of 1 OK created sql table model raw.customer_order_summary [OK in 0.11s]
DuckDB CLIで結果を確認すると、顧客ごとの完了注文サマリテーブルが正しく生成されていることがわかります。テーブル1つのためにここまでやるのか、と思われるかもしれません。しかしテーブルが数十、数百に増えた瞬間、この投資は必ず回収されます。

テストとドキュメント — dbtの真の武器
データ品質テスト
dbtではYAMLにテストを宣言するだけで、dbt build または dbt test により自動検証されます。組み込みテストは4種類: unique、not_null、relationships、accepted_values です。
# models/orders.yml(テストを追加)
version: 2
sources:
- name: raw
schema: raw
tables:
- name: orders
columns:
- name: order_status
data_tests:
- accepted_values:
arguments:
values: ['completed', 'processing', 'returned', 'cancelled']
models:
- name: customer_order_summary
columns:
- name: customer_name
data_tests:
- not_null
dbt test
# FAIL 1 not_null_customer_order_summary_customer_name
# FAIL 1 source_accepted_values_raw_orders_order_status__...
わざと order_status を invalid に書き換えると、テストが正確に検出します。モデルが失敗しても、dbtはテーブルを削除したりロールバックしたりしない点には注意してください。下流モデルはスキップされますが、元テーブルはそのまま残ります。
自動ドキュメントとリネージグラフ
dbt docs generate
dbt docs serve
ブラウザにプロジェクトドキュメントとリネージグラフ(モデル間の依存関係の可視化)が表示されます。YAMLに description を書き込むほど、ドキュメントの品質が向上します。新しいメンバーが数百のSQLをリバースエンジニアリングせずにパイプライン全体を即座に把握できる、これがこの機能の本質的な価値です。
注意点と限界
- dbtは変換ツールです。 取り込み(ingestion)やオーケストレーションは別ツール(Airflow、Dagsterなど)が必要です。
- 学習コストがあります。 Jinja、マクロ、スナップショットまで踏み込むと学習曲線は急になります。
- 小規模プロジェクトにはオーバーキルになり得ます。テーブル5個程度のプロジェクトなら、素のSQLスクリプトの方が適切な場合もあります。
- 日本企業のオンプレミス環境ではdbt Cloudの導入が難しいケースが多く、dbt Core + 自前オーケストレーションの組み合わせが現実的です。閉域網ではDuckDBやPostgreSQLアダプタでPoCから始めることをおすすめします。

次のステップ — ここで止まると半分で終わる
本記事で扱ったのはdbtの表面にすぎません。実務で真価を発揮するには、次のトピックに進んでください。
- Incremental models — 毎回全テーブルを再生成せず、新規・変更レコードのみを処理
- Jinjaテンプレート — 変数、条件分岐、ループでSQLをプログラミング言語のように扱う
- Macros — 再利用可能なSQLロジックの断片、パラメータ化
- Snapshots — 元レコードの時間経過に伴う変化履歴を保持(SCD Type 2)
- Packages — 他チームが作成したモデル・マクロ・テストをそのまま利用
データパイプラインをコードで管理する感覚は、一度身につくと後戻りできません。ローカルのDuckDBで気軽に始めてみてください。
あわせて読みたい記事
- "SageMaker HyperPod Inference Operator, 1クリックインストールからTerraformまで完全整理" — インフラをコードで管理するIaCの観点から、dbtの哲学と通じるものがあります。
- "AIコーディングアシスタントに中国語を入力したのに韓国語で返答された理由" — AIツールがデータパイプラインに関与する際に生じる言語・埋め込みドリフトの問題を扱っています。