なぜ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 を参照してください。

Data engineer visualizing dbt transformation pipeline lineage graph on analytics dashboard IT Technology Image

インストールから最初のモデル実行まで

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つのためにここまでやるのか、と思われるかもしれません。しかしテーブルが数十、数百に増えた瞬間、この投資は必ず回収されます。

Local DuckDB database file connected to dbt Core CLI terminal on Windows developer laptop Coding Session Visual

テストとドキュメント — 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から始めることをおすすめします。

Python virtual environment terminal running dbt init command for new analytics project Algorithm Concept Visual

次のステップ — ここで止まると半分で終わる

本記事で扱ったのはdbtの表面にすぎません。実務で真価を発揮するには、次のトピックに進んでください。

  1. Incremental models — 毎回全テーブルを再生成せず、新規・変更レコードのみを処理
  2. Jinjaテンプレート — 変数、条件分岐、ループでSQLをプログラミング言語のように扱う
  3. Macros — 再利用可能なSQLロジックの断片、パラメータ化
  4. Snapshots — 元レコードの時間経過に伴う変化履歴を保持(SCD Type 2)
  5. Packages — 他チームが作成したモデル・マクロ・テストをそのまま利用

データパイプラインをコードで管理する感覚は、一度身につくと後戻りできません。ローカルのDuckDBで気軽に始めてみてください。

あわせて読みたい記事

本コンテンツは、信頼性の高い情報源をもとにAIツールを活用して作成され、編集者によるレビューを経て公開されています。専門家によるアドバイスの代替となるものではありません。