はじめに
Snowflakeには、クエリ結果を物理的に保持して自動更新する仕組みとして Materialized View と Dynamic Table の2つがあります。どちらも「事前に計算結果を保存しておく」という点では似ていますが、設計思想やユースケースは大きく異なります。
この記事では、両者の違いを整理し、どのような場面でどちらを選ぶべきかを解説します。
想定読者:Snowflakeでデータパイプラインの構築やクエリの高速化を検討しているエンジニア
Materialized Viewとは
Materialized Viewは、単一のベーステーブル に対するクエリ結果を物理的に保存し、ベーステーブルが変更されると 自動的にほぼリアルタイムで更新 される仕組みです。主にクエリの高速化を目的として使用されます。
作成例
CREATE MATERIALIZED VIEW mv_active_users AS
SELECT
user_id,
user_name,
last_login
FROM users
WHERE is_active = TRUE;
特徴
Materialized Viewはベーステーブルの変更を検知し、自動的かつほぼリアルタイムに更新されます。ユーザーが更新タイミングを制御することはできません。
ただし、SQL構文にはかなりの制約があります。
Materialized Viewの主な制約
- 単一テーブル に対してのみ定義可能(JOINは不可)
- ウィンドウ関数、UNION、サブクエリ、一部の集約関数が使用不可
- UDF(ユーザー定義関数)の使用不可
- HAVING句の使用不可
Dynamic Tableとは
Dynamic Tableは、宣言的なデータパイプライン を構築するための仕組みです。SQLのSELECT文でターゲットテーブルの「あるべき状態」を定義すると、Snowflakeがその状態を自動的に維持してくれます。
従来、ストリーム+タスク(Stream + Task)で構築していたETL/ELTパイプラインを、よりシンプルに実現できます。
作成例
CREATE DYNAMIC TABLE dt_user_orders
TARGET_LAG = '10 minutes'
WAREHOUSE = my_wh
AS
SELECT
u.user_id,
u.user_name,
o.order_id,
o.order_date,
o.amount
FROM users u
JOIN orders o ON u.user_id = o.user_id
WHERE o.status = 'completed';
TARGET_LAGによる更新頻度の制御
Dynamic Tableでは TARGET_LAG パラメータで、データの許容遅延時間を指定できます。
-- 1分以内の鮮度を維持
TARGET_LAG = '1 minute'
-- 1時間以内でOK(コスト節約)
TARGET_LAG = '1 hour'
-- 下流のDynamic Tableに合わせる
TARGET_LAG = DOWNSTREAM
コストとデータ鮮度のバランスをユースケースに応じて柔軟に調整できます。
チェーン(多段パイプライン)
Dynamic Tableは他のDynamic Tableを参照でき、多段階の変換パイプラインを宣言的に構築できます。
-- ステージ1:クレンジング
CREATE DYNAMIC TABLE dt_cleaned
TARGET_LAG = '10 minutes'
WAREHOUSE = my_wh
AS
SELECT
user_id,
TRIM(UPPER(user_name)) AS user_name,
email
FROM raw_users
WHERE email IS NOT NULL;
-- ステージ2:集計(dt_cleanedを参照)
CREATE DYNAMIC TABLE dt_summary
TARGET_LAG = DOWNSTREAM
WAREHOUSE = my_wh
AS
SELECT
user_name,
COUNT(*) AS login_count
FROM dt_cleaned
GROUP BY user_name;
このように、DAG(有向非巡回グラフ)構造のパイプラインをSQL定義だけで表現できます。
両者の比較
| 比較項目 | Materialized View | Dynamic Table |
|---|---|---|
| ソース | 単一テーブルのみ | 複数テーブル、JOIN、他のDynamic Table |
| 更新タイミング | 自動(制御不可) |
TARGET_LAG で指定可能 |
| SQL構文の自由度 | 制限が多い | ほぼフルSQL(JOIN、サブクエリ、ウィンドウ関数、CTEなど) |
| パイプラインのチェーン | 不可 | 可能(多段パイプライン) |
| ウェアハウスの指定 | 不要(Snowflakeが自動管理) | 必要(明示的に指定) |
| 主な用途 | クエリの高速化(キャッシュ) | 宣言的なETL/ELTパイプライン |
| DML操作 | 不可 | 不可 |
一言で表すなら、Materialized Viewは 「クエリのキャッシュ」、Dynamic Tableは 「宣言的ETLパイプライン」 です。
ユースケース別の使い分け
Materialized Viewが向いているケース
Materialized Viewは、単一テーブルに対するシンプルなクエリを高速化したい場合に適しています。
- 大規模テーブルに対して頻繁にフィルタリングや単純な集計を行う
- ダッシュボードのバックエンドで、リアルタイムに近い鮮度が必要
- JOINや複雑な変換が不要な場合
Dynamic Tableが向いているケース
Dynamic Tableは、複数ソースを統合する変換パイプラインを構築したい場合に適しています。
- 複数テーブルをJOINしてデータを統合する
- ステージング → クレンジング → 集計のような多段階の変換が必要
- 更新頻度を細かくコントロールしてコストを最適化したい
- 既存のStream + Taskパイプラインをシンプルにしたい
どちらでもなくStream + Taskを選ぶべきケース
Dynamic TableへのDML操作(INSERT、UPDATE、DELETE、MERGE)は禁止されています。宣言的な定義とデータの整合性を保つためです。
以下のようなケースでは、従来のStream + Taskの方が適しています。
- 条件分岐を含む複雑なUPSERTロジックが必要
- 変更データに対して命令的(imperative)に処理を記述したい
- 外部APIの呼び出しなど、SQL以外の処理を組み込みたい
まとめ
Materialized ViewとDynamic Tableは、どちらもデータを物理的に保持して自動更新する仕組みですが、目的と設計思想が異なります。
Materialized View は単一テーブルに対するクエリの高速化に特化しており、シンプルな用途に向いています。 Dynamic Table は複数テーブルの統合や多段階変換を宣言的に定義でき、データパイプラインの構築に適しています。そして、命令的な制御が必要な場合は、従来の Stream + Task を選択しましょう。
要件に応じて適切な仕組みを選ぶことで、Snowflake上のデータパイプラインをよりシンプルかつ効率的に構築できます。