dbt

dbt カスタム Materialization: 独自戦略の作り方

2026-04-19
NicheeLab編集部

dbt の materialization は、モデルの「生成方法」を決める実行戦略です。テーブル、ビュー、インクリメンタルなど既定の選択肢に加えて、要件に合わせたカスタム materialization を実装できます。

本稿では、公式ドキュメントの挙動に沿って安全かつ移植性を意識した設計原則、最小実装、運用の勘所をまとめます。Analytics Engineer 試験対策として出題されやすいポイントも添えています。

カスタム materialization の全体像

materialization は Jinja の特殊ブロックで定義し、モデル側の config(materialized='名前') で選択します。dbt はモデル SQL をコンパイルし、その SQL とターゲットリレーション(this)を引数として materialization ブロックを実行します。

安全な基本パターンは「一時テーブルでビルド → リネームでスワップ → 成果物を返す」です。これにより中途半端な状態の露出を避けられます。

  • 定義場所: macros/materializations/*.sql
  • 命名: {% materialization 名前, adapter='default' %} ... {% endmaterialization %}
  • 実行時コンテキスト: this(出力リレーション), sql(コンパイル済みSQL), target, project, config など
  • 戻り値: return({'relations': [relation1, ...]}) を返す

実行の流れ(コンパイルからスワップまで)

model.sqlJinja/SQL compilematerializationuses: this, sqlcreate temp table from sqldrop/rename atomic swapfinal relation実行の流れ(コンパイルからスワップまで)

設計原則と試験で問われる観点

Analytics Engineer 試験では、既定 materialization とカスタムの違い、適用判断、依存関係や権限の扱いが出題されやすいです。設計時は移植性と失敗時の可観測性を優先します。

特に on-run-end フックや post-hook でやるべき処理を materialization 内で混在させない、グラント適用は明確に分離する、といった責務の切り分けが重要です。

  • 移植性: 倉庫依存の文法を直書きせず、可能な限り create_table_as / create_view_as / adapter API を使う
  • アトミック性: temp → rename スワップで中間露出を抑制
  • 可観測性: statement() と log() を使い、エラー時に痕跡を残す
  • 責務分離: DDL(生成)・DCL(権限)・メタの付与を段階化
種別更新方式典型ユースケース注意点
view(既定)再計算軽量集計や探索権限は基底テーブルに依存
table(既定)再作成安定配信・下流依存が多い場合ビルド時間とストレージ増
incremental(既定)差分/マージイベント/日次パーティションキー管理と再計算戦略が必須
custom任意(スワップ/分割/複合)業務要件に最適化テスト・ロールバックの設計が鍵

最小実装: スワップ型テーブル materialization

もっとも汎用的で安全なカスタムは、コンパイル済み SQL を一時テーブルに書き出し、完成後にリネームで本番リレーションへスワップする方式です。多くのアダプタで利用できるヘルパーマクロと adapter API を使います。

以下は最小実装の例と、モデル側での指定例です。

  • make_temp_relation で一時リレーション名を安全に生成
  • statement() ブロックで DDL をまとめて実行
  • return({'relations': [target]}) を忘れない(カタログ反映に必要)

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 側で代替します。

  • post-hook に DDL 最適化を寄せて副作用を隔離
  • log('message', info=True) で段階ログを出す
  • 失敗時に tmp を残す場合は命名規則と TTL を決めておく
  • full-refresh でも安全に動くよう drop/rename 順を固定化

アダプタ差異とディスパッチ戦略

倉庫固有の最適化を入れたい場合は、default 実装を保ちつつ、必要なアダプタだけ上書きします。materialization ブロックは adapter 引数で分岐定義できます。

この形にすると、未対応アダプタは default を継承し、対応アダプタは最小差分で上書きできます。

  • default を必ず用意して後方互換を担保
  • アダプタ固有 DDL は statement() 内に閉じ込める
  • 共通ロジックは別マクロに分離し再利用性を上げる

アダプタ別の上書き例(概念)

{% 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 配列が欠落しているとカタログや依存に影響します。

  • dbt ls -s model_name で対象確認
  • dbt compile で生成 SQL を先に点検
  • Warehouse 側の RENAME/DROP 権限を事前に検証
  • CI で small dataset を使った smoke test を用意

問題で確認

Analytics Engineer

問題 1

dbt のカスタム materialization を実装した。モデル側で config(materialized='swap_table') を設定し実行したところ、成果物は作成されたが、ドキュメンテーションや依存解決で当該リレーションが認識されない。最も可能性の高い原因はどれか。

  1. materialization で return({'relations': [target]}) を返していない
  2. model.sql 内で is_incremental() を使っていない
  3. post-hook が定義されていない
  4. adapter 引数を default ではなく特定アダプタにしている

正解: 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() で分岐させます。

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

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.