dbt

dbt Materializations の概要: view / table / incremental / ephemeral を実務と試験で使い分ける

2026-04-19
NicheeLab編集部

dbt の materialization は、モデルを「どのようなデータベースオブジェクトとして、どのタイミングで更新するか」を決める仕組みです。選び方を誤ると、コスト・レイテンシ・可用性に直結します。

この記事では view / table / incremental / ephemeral の違いと適切な使い分けを、実務の現場感と Analytics Engineer 試験の観点の両方から整理します。

dbt materialization の基本と選び方

dbt はモデルごとに materialized の種類を切り替えられます。最終的に作られる(または作られない)DBオブジェクトの種類、更新の粒度、権限・コスト特性が変わります。まずは4種類の性質を俯瞰し、要件(再計算コスト、レイテンシ、下流依存、権限)に合わせて選びます。

大雑把な指針として、軽い探索や中間段階は view / ephemeral、安定した提供テーブルは table、継続的に差分更新するファクトや巨大テーブルは incremental が適します。ただし、パーティションやクラスタ、サポートされる戦略(merge / append / insert_overwrite)はアダプタ依存なので、使用するデータプラットフォームのサポート範囲を確認してください(dbt docs と各アダプタのドキュメントが一次情報)。

  • 小さく頻繁に参照、再計算の重さが軽い → view
  • 確定版を安定配布、スナップショット的に使う → table
  • 巨大で全再計算が高コスト、キーやタイムスタンプで差分適用 → incremental
  • 短命な中間整形、DBオブジェクトを増やしたくない → ephemeral
MaterializationDBオブジェクト更新方式コスト/性能の傾向
viewVIEW都度クエリ時に再計算ストレージはゼロ。都度の計算コストは発生
tableTABLEdbt 実行時に全再作成(既定)ストレージ使用。読み取りは高速で安定
incrementalTABLE差分のみ反映(append / merge / insert_overwrite 等)全再計算回避で大幅に効率化
ephemeralなし(CTE にインライン展開)コンパイル時に上流クエリへ埋め込みDBオブジェクト増加なし。最適化次第で高速

4種類の materialization と依存関係(概念図)

sourcestaging_eephemeral (CTE)model_finaltable/incconsumersviewad-hoc queriesephemeral は model_final の SQL に CTE として展開、view は参照時計算、table/incremental は実行時作成/更新

プロジェクト既定とモデル単位の切り替え例

# dbt_project.yml(抜粋)
models:
  my_project:
    +materialized: view    # プロジェクト既定を view に
    marts:
      +materialized: table # サブパスは table で上書き

# models/stg_orders.sql(個別上書き)
{{ config(materialized='ephemeral') }}
select ...

# models/fct_orders.sql
{{ config(materialized='incremental', unique_key='order_id', incremental_strategy='merge') }}
select ... -- is_incremental() で差分を絞り込むのが定石

view: 軽量・柔軟、参照時に計算される

view は CREATE VIEW により論理的なビューを作成します。ストレージを消費せず、参照時に元テーブルへクエリが流れます。開発初期や小規模な中間集計に向きます。

注意点として、重い集計を多段で view に重ねると、下流クエリのたびに上流が再計算され遅くなります。配布用に安定した性能を求める場合は table 化を検討します。アダプタによっては late binding view(依存関係解決を遅延)などのオプションがあり、スキーマ進化時のエラー耐性に差が出ます。必要に応じてアダプタのドキュメントでサポートを確認しましょう。

  • 探索フェーズ、データ量が小さい中間段階に最適
  • 権限は参照先にも影響。ビューだけ許可しても元テーブルの参照権限が必要な場合がある
  • 下流の安定性能が必要なら table へ昇格させる

view モデルの最小構成

{{ config(materialized='view') }}
select
  o.id,
  o.created_at,
  c.country
from {{ ref('raw_orders') }} as o
left join {{ ref('dim_customers') }} as c on o.customer_id = c.id

table: 再作成で安定配布、読み取りが速い

table は dbt 実行時に CREATE TABLE AS SELECT で作成し、モデルの再実行で再作成(既定)されます。読み取り性能と下流の安定性を優先する用途に向きます。

全再作成のコストが無視できない場合は incremental への移行を検討します。パーティション/クラスタの指定はアダプタ依存です(BigQuery の partition_by/cluster_by、Snowflake のクラスタリングキーなど)。指定できる場合は読み取り・再作成コストのバランスを最適化できます。

  • 定期配布・BI 参照の安定性能を重視する最終テーブルに適合
  • 再作成はシンプルだがコスト/実行時間がかかる点に留意
  • 大容量・高頻度更新なら incremental を検討

table モデル(クラスタ指定はアダプタ依存)

{{ config(materialized='table') }}
select * from {{ ref('int_orders_enriched') }}

incremental: 差分のみ反映する戦略と設計の勘所

incremental は初回はテーブルを作成し、以降は差分だけを反映します。差分適用の戦略(incremental_strategy)はアダプタにより異なり、典型的に append(追記)、merge(キーで更新/挿入)、insert_overwrite(パーティション単位の置換)などが使えます。merge では unique_key の指定が重要です。

差分対象の絞り込みは is_incremental() マクロで条件分岐し、更新日時(updated_at)やロード境界(watermark)で制御します。全再計算したい場合は dbt run --full-refresh を用います。スキーマ進化(列追加等)が発生する場合、on_schema_change 設定値(ignore, fail, append_new_columns, sync_all_columns 等。アダプタサポート差あり)を必ず確認してください。

  • unique_key は merge 戦略で必須。append では不要だが重複管理は別途必要
  • is_incremental() 内で updated_at >= 最大処理済み時刻 などの条件を記述
  • パーティション/クラスタを併用して I/O を最適化(アダプタ依存)
  • --full-refresh で安全に全再構築。運用手順に組み込む

merge 戦略の典型パターン(キー更新あり)

{{ config(
    materialized='incremental',
    unique_key='order_id',
    incremental_strategy='merge'
) }}

with src as (
  select * from {{ ref('stg_orders') }}
  {% if is_incremental() %}
    where updated_at >= (
      select coalesce(max(updated_at), '1900-01-01') from {{ this }}
    )
  {% endif %}
)

select * from src

ephemeral: CTE へインライン展開される中間モデル

ephemeral はデータベースにオブジェクトを作らず、依存先のモデル SQL に共通表式(CTE)としてインライン展開されます。中間ステップをファイルとして分離しつつ、DB 内のオブジェクトを増やしたくない場合に有効です。

注意点として、巨大な処理を多数の ephemeral に詰め込むと、単一クエリの長大化や最適化の難しさに繋がります。また、外部ツールから直接参照したり権限付与したりはできません。挙動の確認には dbt compile で生成 SQL を見るのが手早いです。

  • 短命なクリーニング・フィルタ・正規化に適合
  • 外部参照・権限付与・インデックス設定はできない
  • 重い処理はテーブル化 or ビュー化して分散させる

ephemeral モデルと利用側の例

-- models/_int_orders_ephemeral.sql
{{ config(materialized='ephemeral') }}
select *
from {{ ref('stg_orders_raw') }}
where is_valid = true

-- models/fct_orders.sql(利用側にCTEとして展開される)
{{ config(materialized='table') }}
with cleaned as (
  select * from {{ ref('_int_orders_ephemeral') }}
)
select * from cleaned

運用設計と Analytics Engineer 試験の観点

試験では、どの要件にどの materialization が適するか、差分戦略と unique_key、--full-refresh の意味、ephemeral の特性が頻出です。実務では、開発初期は view/ephemeral、安定後に table/incremental へ昇格する流れが安全です。

環境別(開発/本番)で materialization を切り替える場合は、変数や target.name を条件にして config を分岐させます。依存の広がり・再計算コスト・権限モデルを意識して、計画的に昇格/降格を管理します。

  • default はプロジェクト/フォルダで設定し、例外をモデルで上書き
  • 大規模モデルは最初から incremental 前提で設計(キー/水位を決める)
  • 列追加時の on_schema_change はアダプタのサポート差を確認
  • 実行コマンド: dbt run, dbt run --full-refresh, セレクタで部分実行(dbt run -s tag:incremental など)

環境で materialization を切り替える例

{{ config(
    materialized= (target.name == 'prod') and 'incremental' or 'view',
    unique_key= (target.name == 'prod') and 'id' or none
) }}
select * from {{ ref('stg_items') }}

問題で確認

Analytics Engineer

問題 1

巨大な fact テーブルを毎日更新する。更新対象は updated_at で識別でき、既存行の上書きも必要。処理時間とコストを最小化しつつ重複を避ける設計として最も適切なのはどれか?

  1. モデルを incremental にし、incremental_strategy=merge と unique_key を設定。is_incremental() で updated_at による差分だけを対象にする
  2. モデルを table にし、毎回フル再作成する。差分はアプリ側で制御する
  3. モデルを view にして参照時に都度計算する。updated_at フィルタは下流ツールに任せる
  4. 中間整形をすべて ephemeral にして、最終も ephemeral のまま配布する

正解: A

差分更新と既存行の上書きが必要な要件には incremental(merge 戦略)+ unique_key が定石。is_incremental() で更新日時に基づく範囲を絞ることで、実行時間とコストを抑えつつ重複を防げる。table は毎回フル再作成で非効率、view は参照時コストが高く一貫性能を確保しづらい。ephemeral は配布用の最終オブジェクトにならない。

よくある質問

開発では view、本番では incremental に切り替えたい。どう書けばよい?

config で target.name(環境名)や vars を条件に分岐します。例: {{ config(materialized=(target.name=='prod') and 'incremental' or 'view', unique_key=(target.name=='prod') and 'id' or none) }} のように書くと、prod では incremental、他は view になります。

incremental モデルで列が追加されたらどうなる?

挙動は on_schema_change とアダプタのサポートに依存します。ignore(無視)、fail(失敗)、append_new_columns(新列の追加)、sync_all_columns(差分同期)などがあり、利用可否はアダプタで異なります。確実に反映したい場合は --full-refresh、またはサポートされていれば append_new_columns / sync_all_columns を設定してください。

ephemeral と view の使い分けは?

ephemeral は中間処理をCTEとしてインライン化しDBオブジェクトを増やしません。下流から直接参照・権限付与が不要で、処理が軽いときに向きます。view は外部から直接参照可能で、共通ロジックの共有やアクセス制御が必要なときに適します。重い処理や安定性能が必要なら table/incremental を検討します。

この記事で学んだ内容を問題で確認しましょう

16,000問以上の問題で実力チェック

無料で問題を解いてみる
この記事の著者

NicheeLab編集部

データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。


関連記事
dbt

dbt Model の基礎: SQL で定義する変換の最小単位

Analytics Engineer 向けに、dbt Model の定義、マテリアライゼーション、依存関係、インクリメン...

dbt

dbt Analytics Engineer 試験ガイド: 出題範囲・配点・申込の実務視点

dbt Analytics Engineer 認定の出題範囲、配点の考え方、申込から受験までの流れを、公式ドキュメントの...

dbt

dbt Cloud と dbt Core の違いと選び方:Analytics Engineer 試験に効く要点

dbt Cloud と dbt Core の機能差を、実務と資格対策の両面から整理。スケジューリング、IDE、RBAC、...

dbt

dbt プロジェクト構造ガイド: models / seeds / macros の実務レイアウト

Analytics Engineer 向けに、dbt プロジェクトのディレクトリ構造と命名規約、dbt_project....

dbt

dbt_project.yml の読み方:主要設定と命名を最短で掴む

dbt_project.yml の必須キー、命名解決(database.schema.identifier)、設定優先度...

dbtの記事一覧 (100件)
© 2026 NicheeLab All rights reserved.