dbt のモデルは、SQL の SELECT 文で定義する変換の最小単位です。1ファイル=1モデル=1つのSELECTという単純なルールが、変更に強いDAGと再現性の高い開発体験をもたらします。
本稿では、モデルの基本設計からマテリアライゼーション選択、ref/source による依存関係管理、インクリメンタルの基礎、テストと運用まで、試験で問われやすい観点を実務ベースで整理します。
dbt におけるモデルは、models ディレクトリ配下の .sql ファイルで定義する SELECT 文です。Jinja を用いたテンプレート化が可能で、ref や source、マクロ、変数、config ブロックを組み合わせて環境に依存しない変換を表現します。
モデルは単一の SELECT を基本とし、DML を直接書かず、永続化の方法はマテリアライゼーションに委ねます。命名はレイヤ別の接頭辞(stg_, int_, dim_, fct_ など)とスネークケースが管理上有効です。資格試験では、モデルが DAG のノードになり、マテリアライゼーションによりオブジェクト化の有無が変わる点がよく問われます。
最小のモデル例(staging レイヤの view)
{{ config(materialized='view', tags=['staging']) }}
select
id,
user_id,
event_type,
event_ts
from {{ source('raw', 'events') }}
where event_ts >= dateadd(day, -30, current_timestamp)dbt のモデルは materialized パラメータで永続化の方法を選べます。代表は view, table, incremental, ephemeral。実体化の度合い・リフレッシュコスト・実行速度・下流からの参照可否が主なトレードオフです。
プロジェクト全体の既定値は dbt_project.yml で上書きし、例外的なモデルはファイル内の config で個別指定します。試験では、ephemeral は中間オブジェクトを作らずコンパイル時に上位モデルへ CTE 内挿される点、incremental は部分更新である点が頻出です。
| 種類 | 特徴 | リフレッシュ戦略 | ストレージコスト |
|---|---|---|---|
| view | 常に最新を参照(仮想ビュー) | 再作成は軽量、実データは保有しない | 低 |
| table | 物理テーブルを作成 | 全件再作成(完全リビルド) | 中〜高 |
| incremental | 差分適用(部分更新) | 新規/更新分のみ投入(MERGE等) | 中 |
| ephemeral | オブジェクト非作成(CTE内挿) | 該当なし | ゼロ |
マテリアライゼーション指定(モデル内とプロジェクト設定)
-- モデルファイル内で個別指定
{{ config(materialized='incremental', unique_key='id') }}
select * from {{ ref('stg_orders') }}
# dbt_project.yml で既定値をレイヤ単位に設定
models:
my_project:
+materialized: table
staging:
+materialized: view
marts:
+materialized: tableref は他モデルを論理名で参照し、コンパイル時に正しいスキーマ・オブジェクト名へ解決しつつ、依存関係をDAGに登録します。source は外部の生データオブジェクトを宣言的に取り込みます。これらにより、環境差異を吸収しつつ正しい順序でビルドされます。
dbt build はモデル作成・テスト・スナップショット・シードを依存順に包括実行します。選択子(--select/--exclude)と組み合わせて、影響範囲のみの安全なビルドが可能です。
モデルDAGのイメージ
source の宣言と ref/source の併用例
# models/sources.yml
version: 2
sources:
- name: raw
database: RAW_DB
schema: RAW_SCHEMA
tables:
- name: orders
- name: users
-- models/stg_orders.sql
{{ config(materialized='view', tags=['staging']) }}
select *
from {{ source('raw', 'orders') }}
-- models/fct_orders.sql
{{ config(materialized='table', tags=['marts']) }}
select
o.id,
o.user_id,
u.country,
o.total_amount,
o.order_ts
from {{ ref('int_orders_enriched') }} o
join {{ ref('dim_users') }} u on o.user_id = u.user_idincremental は大規模テーブルの再計算コストを抑えるための部分更新戦略です。unique_key やパーティション列で新規・更新レコードを特定し、is_incremental() を用いて初回と以降の処理を分岐させます。実際の差分適用方法(MERGE/INSERT OVERWRITE 等)はアダプタ実装に依存します。
スキーマが変わる可能性がある場合は on_schema_change の扱いを検討します。全件作り直したい場合は --full-refresh を付けて実行します。試験では、インクリメンタルが『部分更新』『初回フルビルド』『unique_key で更新検出』をキーワードとして問われます。
インクリメンタルモデルの典型パターン
{{ config(
materialized='incremental',
unique_key='id',
on_schema_change='sync_all_columns'
) }}
with src as (
select * from {{ ref('stg_orders') }}
{% if is_incremental() %}
where order_ts > (select coalesce(max(order_ts), '1900-01-01') from {{ this }})
{% endif %}
),
calc as (
select id, user_id, total_amount, order_ts, total_amount * 0.1 as tax
from src
)
select * from calcモデルの品質はスキーマファイル(yml)で宣言的にテストします。not_null, unique, relationships などの汎用テストに加え、必要なら SQL を書く単体(singular)テストも利用します。dbt build はモデルとテストを依存順に実行します。
ドキュメントはカラム記述やソース説明を yml に記載し、dbt docs generate/serve でブラウズ可能です。説明文は将来のメンテナと試験の双方で重要な資産になります。
スキーマテスト例(generic と singular)
# models/marts/schema.yml
version: 2
models:
- name: fct_orders
description: 受注のファクトテーブル
columns:
- name: id
tests: [not_null, unique]
- name: user_id
tests:
- relationships:
to: ref('dim_users')
field: user_id
# tests/high_value_orders.sql(singular テスト)
select 1
where exists (
select 1 from {{ ref('fct_orders') }} where total_amount < 0
)dbt run はモデルを、dbt build はモデル・テスト・スナップショット・シードを依存順に実行します。--select/--exclude で影響範囲を限定し、タグやパス、依存グラフ演算子(+ や @)を活用します。state:modified を用いると変更検出に基づく選択が可能です。
パフォーマンス面では、上流の集計やフィルタはなるべく早いレイヤ(staging では軽く、intermediate で正規化・集約、marts でビジネスロジック)に配分します。倉庫固有のパーティション/クラスタキー設定はモデルの DDL オプション(アダプタ依存の config)で管理します。
代表的な選択子の例
# 変更のあったモデルとその下流をビルド
dbt build --select state:modified+
# あるモデルの上流も含めて実行
dbt run --select +int_orders_enriched
# 2つのタグを持つモデルのみ
dbt run --select tag:marts,tag:critical
# 失敗したノードを再実行
dbt build --select result:errorAnalytics Engineer
問題 1
下流の1つのモデルだけで使う小さな補助変換を作りたい。中間オブジェクトをウェアハウス上に作成せず、コストを最小化したい。この補助変換に最適なマテリアライゼーションはどれか。
正解: D
ephemeral は物理オブジェクトを作らず、親モデルにCTEとして内挿されます。中間テーブルの作成・クリーンアップが不要で、単一下流でのみ使う補助変換に最適です。
モデル名を変更したいが下流への影響を抑えるには?
ref を使っていれば論理名解決のためビルド順序は自動調整されます。リネーム時は git の差分と state:modified セレクタで影響範囲をビルドし、dbt test で関係テストが通ることを確認します。必要に応じて alias で旧物理名を維持しつつ移行も可能です。
インクリメンタルでカラムが増減した場合は?
on_schema_change 設定を用います。sync_all_columns などの動作はアダプタ依存です。重大な変更や型変更を伴う場合は --full-refresh を選び、マイグレーションの安全性を優先します。
ソースの遅延データ(遅延到着更新)に対応するには?
unique_key を定義した上で is_incremental() 内の取り込み条件を『最大タイムスタンプより少し前』まで拡張する、もしくは適切な MERGE 条件を設定します。必要に応じて定期的なフルリフレッシュやウィンドウ再計算のバッチを併用します。
NicheeLab編集部
データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。
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)、設定優先度...
dbt profiles.yml と接続管理:環境別のベストプラクティス
dbt Core/Cloud で profiles.yml を用いて dev/stg/prod など環境別に接続を切り替...