dbt の table マテリアライゼーションは、毎回フルにテーブルを作り直すシンプルさが強みです。一方で、データ量や基盤ごとの置換方式によってコストやロック挙動が変わるため、正しく理解して設計する必要があります。
この記事では、Analytics Engineer 受験者が押さえるべき公式挙動に基づき、再構築パターンと性能特性、選定基準、運用のコツを体系的に解説します。
table は、モデルを実行するたびにテーブル全体を再作成します。多くのアダプタでは CREATE OR REPLACE TABLE もしくは一時テーブルを作ってからアトミックにリネームする方式で、既存テーブルを丸ごと置き換えます。これにより、下流からは一貫したスナップショットとして参照されます。
インクリメンタルではないため、差分更新は行いません。スキーマ変更は再作成時に反映され、依存関係の解決は dbt の依存グラフに従います。ドキュメントの付与や権限付与は、アダプタがサポートしていれば post-hook や grants、persist_docs 設定で適用されます。
| マテリアライゼーション | 更新方式 | 主な用途 |
|---|---|---|
| table | 毎回フル再作成 | 安定したスナップショット、集約・中間成果物 |
| view | クエリ都度評価 | 軽量な論理レイヤ、検証・小規模参照 |
| incremental | 差分のみ挿入/マージ | 大規模データ、低コスト反復更新 |
| ephemeral | 実体を持たない (CTE 展開) | 小さな前処理・再利用ロジック |
table ビルドの高レベル流れ
基本的な table モデルの設定例 (models/orders_rollup.sql)
{{ config(
materialized = 'table',
tags = ['daily'],
persist_docs = {'relation': true, 'columns': true}
) }}
with base as (
select * from {{ ref('stg_orders') }}
)
select customer_id,
date_trunc(day, order_ts) as order_date,
count(*) as order_cnt,
sum(amount) as order_amount
from base
group by 1,2table は通常実行時も “フルリフレッシュ” 相当の挙動です。つまり --full-refresh フラグの有無で処理が変わるのは主に incremental であり、table では常に再作成されます。
置換の安全性はアダプタにより異なります。CREATE OR REPLACE がある場合は単一文で置換可能、未対応の場合は tmp 作成→リネームや drop→create をトランザクション内で行います。これにより、失敗時には旧テーブルが残る・tmp が残置されるなどのパターンが発生し得ます。
| アダプタ例 | 置換パターン | 原子的/トランザクション性の考慮 |
|---|---|---|
| BigQuery | CREATE OR REPLACE TABLE AS SELECT | 単一文置換。権限・メタ更新は文後に適用 |
| Snowflake | CREATE OR REPLACE / 一時表→RENAME | リネームは原子的。失敗時は旧表が残ることが多い |
| Redshift/Postgres | tmp 作成→RENAME または DROP→CREATE | トランザクション内で整合性を担保 |
| Databricks (Delta) | CREATE OR REPLACE TABLE AS SELECT | メタ更新は高速。最適化は別途 OPTIMIZE/VACUUM |
tmp→rename 戦略の簡易シーケンス
コンパイル後に近いイメージ (アダプタにより差異あり)
-- 例: CREATE OR REPLACE をサポートする場合
create or replace table analytics.orders_rollup as
select ...;
-- 例: tmp→rename を使う場合
create table analytics.orders_rollup__dbt_tmp as select ...;
alter table analytics.orders_rollup__dbt_tmp rename to analytics.orders_rollup;table は全件再作成のため、入力テーブルのスキャン量と集約処理がコスト・時間の主因になります。最適化の基本は、上流での前集約・不要列の削減・フィルタの早期適用です。物理設計としては、アダプタがサポートする partition/cluster/distribution の設定が、読み取りや書き込みのパスに影響します。
ただし、table である以上、モデル SQL が完全な結果セットを返す必要があります。単純に WHERE で最近分だけ読む設計は、結果が完全であるという要件を満たすときにのみ採用可能です(例えば上流がスナップショット累積を保持している場合など)。
| レバー | 制御方法 (例) | 注意点 |
|---|---|---|
| 列・行の削減 | 上流モデルで select 列最小化、早期 where | 結果の完全性を保つロジックであること |
| パーティション/クラスタ | config(partition_by=..., cluster_by=...) | 書き込み時のソート/シャッフル負荷に留意 |
| 計算リソース | Snowflake: 仮想倉庫サイズ、BigQuery: スロット/定額 | 短時間に集約してビルド時間を短縮 |
| テーブル整理 | Databricks: OPTIMIZE/VACUUM, Snowflake: メンテ | メンテは実行ウィンドウ外で行う |
性能ボトルネックの俯瞰
BigQuery でのパーティション・クラスタ設定例
{{ config(
materialized = 'table',
partition_by = { 'field': 'order_date', 'data_type': 'date' },
cluster_by = ['customer_id']
) }}
select ... from {{ ref('stg_orders') }}table はシンプルで堅牢ですが、データ量が増えるとビルド時間と費用が増加します。日次〜時間単位での再計算が許容でき、全件再作成のコストが現実的な範囲に収まるなら有力です。逆に、到着データが多く、部分更新で十分に正を保てるなら incremental の方が有利です。
また、頻繁なスキーマ変更やロジックの試行錯誤が続くフェーズでは、table の再作成の方が変更伝搬が簡単です。安定期には、ホットパスだけ incremental に寄せるハイブリッドも現実解です。
| 条件 | table 向き | incremental 向き |
|---|---|---|
| データ量/到着頻度 | 小〜中、日次/時間単位 | 大、準リアルタイム |
| スキーマ変化の頻度 | 高い (毎回再作成で同期) | 低〜中 (差分管理が安定) |
| SLA/遅延要件 | 分〜時間単位で許容 | 短い遅延が必要 |
| コスト最適化 | シンプル運用優先 | コンピュート節約優先 |
選定の判断フロー(簡易)
環境や変数で materialization を切替える例
{% set mat = var('mat_override', 'table') %}
{{ config(materialized = mat) }}
select ... from {{ ref('stg_orders') }}
-- 実行例: dbt run --vars "{mat_override: incremental}"table は物理リレーションとして存在するため、権限付与やコメント付与、カラム記述の永続化を設定で行えます。アダプタが grants と persist_docs をサポートしていれば、再作成後にも自動で適用されます。
制約・インデックスはアダプタ依存です。サポートされる場合は constraints や indexes 設定、あるいは post-hook で DDL を流します。スキーマテストは .yml で定義し、dbt build でモデルと一緒に検証します。
| 機能 | 設定例 | 補足 |
|---|---|---|
| 権限付与 | config(grants={'select': ['analyst_role']}) | アダプタが grants をサポートしている必要 |
| ドキュメント | persist_docs={'relation': true, 'columns': true} | コメント付与が可能な基盤で有効 |
| 制約/索引 | constraints / indexes / post-hook | 対応可否はアダプタ・バージョンに依存 |
dbt build による一体運用
モデルとスキーマテスト・権限の組合せ例
{{ config(
materialized='table',
grants={'select': ['ANALYST_ROLE']},
persist_docs={'relation': true, 'columns': true}
) }}
select ...
# models/schema.yml
version: 2
models:
- name: orders_rollup
columns:
- name: customer_id
tests: [not_null]
- name: order_date
tests: [not_null]
- name: order_amount
tests: [not_null]再作成はコンピュート負荷が高いため、他ジョブと重ならない時間帯にスケジュールし、ウェアハウスやスロットを適切に割り当てます。並列実行は依存関係が解決される範囲で有効ですが、同一スキーマでの大量同時置換はロック競合の原因になり得ます。
失敗時の残置物や半端な状態は、置換方式により異なります。tmp→rename 方式では、rename 前の失敗で旧テーブルが残り tmp が残置、rename 後の失敗で新テーブルは有効というケースが一般的です。CREATE OR REPLACE では置換自体が完了しなければ旧テーブルが残るのが通常です。
| シナリオ | 期待される状態 | 対応 |
|---|---|---|
| tmp 作成後に失敗 | 旧テーブルは存続、tmp が残置 | tmp のクリーンアップ自動化 |
| rename 後に post-hook で失敗 | 新テーブルは有効、hook 未適用 | hook のリトライ・冪等化 |
| CREATE OR REPLACE 失敗 | 旧テーブルが有効 | 原因調査後に再実行 |
障害と残置物のタイムライン
選択子・並列度の運用例
# 変更のあったモデルだけを対象
$ dbt build --select state:modified+
# 依存をまたいだ並列度(実行基盤に応じてジョブ並列)
$ dbt build --threads 8Analytics Engineer
問題 1
dbt の table マテリアライゼーションに関する説明として最も適切なのはどれか。BigQuery を想定する。
正解: C
table は常にフル再作成であり、BigQuery では CREATE OR REPLACE TABLE による置換が一般的です。差分更新は incremental の挙動であり、on_schema_change は主に incremental 向けの設定です。
table でカラムを追加・削除した場合、どう反映されるか?
table は毎回テーブルを再作成するため、モデル SQL のスキーマがそのまま新しいテーブル定義になります。アダプタがコメント付与をサポートしていれば persist_docs で説明も更新されます。
大規模データで table を使う際のコスト対策は?
上流の前集約・列削減・早期フィルタを徹底し、パーティション/クラスタ設計を適用します。実行ウィンドウを分け、計算リソースを短時間スケールしてビルド時間を縮めるのも有効です。完全結果を返すロジックを維持しつつ、不要な再計算を避ける設計にします。
table と incremental の切り替えは安全にできるか?
可能です。移行時は一度 --full-refresh で完全再作成したうえで、incremental に切り替え、キーや on_schema_change、マージ条件を明確にします。逆に複雑化した incremental を一時的に table に戻して検証する運用もよく行われます。
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)、設定優先度...