dbt の materialization は、モデルの「生成方法」を決める実行戦略です。テーブル、ビュー、インクリメンタルなど既定の選択肢に加えて、要件に合わせたカスタム materialization を実装できます。
本稿では、公式ドキュメントの挙動に沿って安全かつ移植性を意識した設計原則、最小実装、運用の勘所をまとめます。Analytics Engineer 試験対策として出題されやすいポイントも添えています。
materialization は Jinja の特殊ブロックで定義し、モデル側の config(materialized='名前') で選択します。dbt はモデル SQL をコンパイルし、その SQL とターゲットリレーション(this)を引数として materialization ブロックを実行します。
安全な基本パターンは「一時テーブルでビルド → リネームでスワップ → 成果物を返す」です。これにより中途半端な状態の露出を避けられます。
実行の流れ(コンパイルからスワップまで)
Analytics Engineer 試験では、既定 materialization とカスタムの違い、適用判断、依存関係や権限の扱いが出題されやすいです。設計時は移植性と失敗時の可観測性を優先します。
特に on-run-end フックや post-hook でやるべき処理を materialization 内で混在させない、グラント適用は明確に分離する、といった責務の切り分けが重要です。
| 種別 | 更新方式 | 典型ユースケース | 注意点 |
|---|---|---|---|
| view(既定) | 再計算 | 軽量集計や探索 | 権限は基底テーブルに依存 |
| table(既定) | 再作成 | 安定配信・下流依存が多い場合 | ビルド時間とストレージ増 |
| incremental(既定) | 差分/マージ | イベント/日次パーティション | キー管理と再計算戦略が必須 |
| custom | 任意(スワップ/分割/複合) | 業務要件に最適化 | テスト・ロールバックの設計が鍵 |
もっとも汎用的で安全なカスタムは、コンパイル済み SQL を一時テーブルに書き出し、完成後にリネームで本番リレーションへスワップする方式です。多くのアダプタで利用できるヘルパーマクロと adapter API を使います。
以下は最小実装の例と、モデル側での指定例です。
macros/materializations/swap_table.sql とモデル例
-- macros/materializations/swap_table.sql
{% materialization swap_table, adapter='default' %}
{% set target = this %}
{% set tmp = make_temp_relation(target) %}
{# 既存の一時テーブルを削除 #}
{% do adapter.drop_relation(tmp) %}
{# 一時テーブルへ作成(コンパイル済みSQL = sql) #}
{% call statement('create_tmp', fetch_result=False) %}
{{ create_table_as(tmp, sql) }}
{% endcall %}
{# 旧テーブルを削除してスワップ #}
{% do adapter.drop_relation(target) %}
{% do adapter.rename_relation(tmp, target) %}
{# 成果物を返す #}
{{ return({'relations': [target]}) }}
{% endmaterialization %}
-- models/fct_orders.sql
{{ config(materialized='swap_table') }}
select *
from {{ ref('stg_orders') }}
where order_status != 'CANCELLED';運用では、フックや権限適用、失敗時のクリーンアップ設計が重要です。post-hook での ANALYZE/OPTIMIZE のような付帯処理は、materialization 本体と分離し、再実行可能性を高めます。
権限は grants をモデル設定で定義し、可能なら apply_grants を materialization の最後に呼び出します。未対応アダプタでは post-hook 側で代替します。
倉庫固有の最適化を入れたい場合は、default 実装を保ちつつ、必要なアダプタだけ上書きします。materialization ブロックは adapter 引数で分岐定義できます。
この形にすると、未対応アダプタは default を継承し、対応アダプタは最小差分で上書きできます。
アダプタ別の上書き例(概念)
{% materialization swap_table, adapter='default' %}
{# 共通のスワップ実装 #}
...
{% endmaterialization %}
{% materialization swap_table, adapter='snowflake' %}
{# Snowflake 向けに CLUSTER BY 等を追加するなど、差分のみ記述 #}
...
{% endmaterialization %}新しい materialization は小さなモデルで動作確認し、dbt run -m model_name --full-refresh と通常実行の双方で振る舞いを確かめます。
問題が起きたら、生成 SQL と statement ログ、tmp リレーションの残骸を確認します。戻り値の relations 配列が欠落しているとカタログや依存に影響します。
Analytics Engineer
問題 1
dbt のカスタム materialization を実装した。モデル側で config(materialized='swap_table') を設定し実行したところ、成果物は作成されたが、ドキュメンテーションや依存解決で当該リレーションが認識されない。最も可能性の高い原因はどれか。
正解: A
カスタム materialization は処理後に関係リストを返す必要があります。return({'relations': [target]}) を返さないとカタログや依存に成果物が伝搬しません。is_incremental() や post-hook の有無は直接原因になりません。adapter 指定は default でなくても、該当アダプタにマッチしていれば動作します。
incremental をカスタム materialization に置き換えるべきケースは?
標準の incremental が提供する差分/マージ機能で足りず、特定の業務ロジック(期間別入れ替え、2 段階検証後のコミット、複数テーブル同時スワップなど)を原子性高く実現したい場合です。まずは既定機能で代替できないかを確認してください。
権限付与(grants)は materialization 内と post-hook のどちらで行うべき?
再実行性と責務分離の観点から、モデルの grants 設定と apply_grants を materialization の最後に挿入するか、post-hook に分離します。環境やアダプタ対応状況で選び、どちらかに統一してください。
一時テーブルを rename できない(ロックや権限で失敗)場合の対処は?
まず DROP/RENAME 権限を確認し、競合ジョブを避けるスケジューリングを行います。やむを得ない場合は CREATE OR REPLACE 方式へフォールバックするブランチを用意し、statement() で分岐させます。
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)、設定優先度...