Snowflakeのデータパイプラインを構築する際、Dynamic TablesとStreams+Tasksのどちらを採用するかは設計上の重要な分岐点です。 両者はまったく異なるパラダイムで動作し、適するユースケースも異なります。 この記事では7つの比較軸で両者の違いを整理し、設計判断のフレームワークを提供します。
Dynamic Tablesは宣言型アプローチです。 「このSELECT文の結果を常に最新に保ってほしい」と宣言するだけで、 Snowflakeが変更検知・増分処理・リフレッシュスケジューリングをすべて管理します。
Streams+Tasksは手続型アプローチです。 StreamでCDC(変更データキャプチャ)を取得し、TaskでMERGE/INSERT文を実行する手順を明示的にコーディングします。 変換ロジック・実行順序・エラーハンドリングのすべてを開発者が制御します。
-- 宣言型: Dynamic Table
CREATE DYNAMIC TABLE gold_revenue
TARGET_LAG = '10 minutes'
WAREHOUSE = etl_wh
AS
SELECT region, SUM(amount) AS total_revenue
FROM silver_orders
GROUP BY region;
-- 手続型: Stream + Task
CREATE STREAM orders_stream ON TABLE silver_orders;
CREATE TASK merge_revenue
WAREHOUSE = etl_wh
SCHEDULE = 'USING CRON */10 * * * * UTC'
WHEN SYSTEM$STREAM_HAS_DATA('orders_stream')
AS
MERGE INTO gold_revenue t
USING (
SELECT region, SUM(amount) AS total_revenue
FROM orders_stream
GROUP BY region
) s ON t.region = s.region
WHEN MATCHED THEN UPDATE SET t.total_revenue = t.total_revenue + s.total_revenue
WHEN NOT MATCHED THEN INSERT (region, total_revenue) VALUES (s.region, s.total_revenue);| 観点 | Dynamic Tables | Streams + Tasks |
|---|---|---|
| 遅延制御 | TARGET_LAG(最大許容遅延を宣言) | CRONスケジュール(実行間隔を明示指定) |
| 「毎時0分に実行」の指定 | 不可(ラグベースの制御のみ) | 可能(CRON式で厳密に指定) |
| データ変更がないときの動作 | リフレッシュをスキップ(自動判定) | WHEN条件で明示的にスキップ |
| DAG全体のSLA管理 | 末端のTARGET_LAGから自動逆算 | 各Taskのスケジュールを個別設計 |
| 項目 | Dynamic Tables | Streams + Tasks |
|---|---|---|
| 初期コード量 | 少ない(CREATE DYNAMIC TABLE + SELECT) | 多い(Stream + Task + MERGE/INSERT文) |
| スキーマ変更時の対応 | SELECT文を修正してREPLACE | Stream再作成 + Task修正 + 再スケジュール |
| 障害時のリカバリ | 自動リトライ、手動REFRESH可 | Task失敗ログ確認→Stream位置確認→手動再実行 |
| テスト容易性 | SELECT文単体でテスト可能 | Stream消費はべき等でないため再テストに注意 |
Dynamic TablesではSELECT文にJOINを自由に記述でき、Snowflakeが依存テーブル全体を追跡します。 Streams+Tasksの場合、Streamは単一テーブル(またはビュー)に対して作成するため、 複数テーブルの変更をJOINするにはStreamを複数作成してTask内で結合するか、 変更検知のタイミングがずれる問題に対処する設計が必要です。
-- Dynamic Tables: JOINを含むSELECTをそのまま宣言
CREATE DYNAMIC TABLE enriched_orders
TARGET_LAG = '5 minutes'
WAREHOUSE = etl_wh
AS
SELECT
o.order_id,
o.order_date,
p.product_name,
p.category,
o.quantity * p.unit_price AS line_total
FROM raw_orders o
JOIN raw_products p ON o.product_id = p.product_id;
-- Streams/Tasks: 複数Streamの管理が必要
CREATE STREAM orders_cdc ON TABLE raw_orders;
CREATE STREAM products_cdc ON TABLE raw_products;
-- → Task内で両Streamを参照し、MERGEロジックを実装
-- → 片方のStreamだけにデータがあるケースの処理が必要Dynamic TablesのIncrementalリフレッシュはSnowflakeが自動判定・自動実行します。 開発者はリフレッシュモードを意識せずSELECT文を書くだけです。 ただし、WINDOW関数やSELECT DISTINCTを含むクエリはFull Refreshにフォールバックする点に注意が必要です。
Streams+Tasksでは、StreamがINSERT/UPDATE/DELETEの変更行をMETADATA$ACTION列等で提供するため、 MERGE文で精密な増分処理を記述できます。DELETE操作の追跡やUPDATE前後の値の比較など、 きめ細かい制御が可能です。
| コスト項目 | Dynamic Tables | Streams + Tasks |
|---|---|---|
| コンピュートコスト | 指定ウェアハウスのクレジット | 指定ウェアハウスのクレジット |
| ストレージコスト | 実体テーブルのストレージ | ターゲットテーブルのストレージ |
| 変更なし時のコスト | リフレッシュスキップ(ほぼ0) | WHEN条件でスキップ(ほぼ0) |
| Full Refresh発生時 | 大規模テーブルでコスト増大の可能性 | Streamベースで増分のみ処理 |
| 監視用ビュー | DYNAMIC_TABLE_REFRESH_HISTORY | TASK_HISTORY / STREAM_HAS_DATA |
-- Dynamic Tablesのリフレッシュ監視
SELECT name, refresh_action, state, data_timestamp
FROM TABLE(INFORMATION_SCHEMA.DYNAMIC_TABLE_REFRESH_HISTORY())
WHERE state = 'FAILED'
ORDER BY refresh_start_time DESC
LIMIT 10;
-- Streams/Tasksの実行監視
SELECT name, state, error_message, scheduled_time
FROM TABLE(INFORMATION_SCHEMA.TASK_HISTORY(
SCHEDULED_TIME_RANGE_START => DATEADD(HOUR, -24, CURRENT_TIMESTAMP())
))
WHERE state = 'FAILED'
ORDER BY scheduled_time DESC;パイプラインの要件は?
│
├─ SELECT文で表現可能な変換か?
│ ├─ YES → JOINや集計が中心か?
│ │ ├─ YES → Dynamic Tables推奨
│ │ └─ NO(条件分岐・外部連携あり)→ Streams/Tasks
│ └─ NO(ストアドプロシージャが必要)→ Streams/Tasks
│
├─ 厳密なCRONスケジュールが必要か?
│ ├─ YES(毎時0分に実行等)→ Streams/Tasks
│ └─ NO(最大遅延指定でOK)→ Dynamic Tables
│
├─ DELETE/UPDATEの変更前後の値を追跡する必要があるか?
│ ├─ YES → Streams/Tasks(METADATA$ACTION / $ISUPDATE)
│ └─ NO → Dynamic Tables
│
└─ 運用負荷を最小化したいか?
├─ YES → Dynamic Tables
└─ 細かい制御が必要 → Streams/Tasks| 比較軸 | Dynamic Tables | Streams + Tasks |
|---|---|---|
| 1. パラダイム | 宣言型 | 手続型 |
| 2. SLA制御 | TARGET_LAG(最大遅延) | CRON(実行タイミング) |
| 3. 複雑さ | 低い(SELECT文のみ) | 高い(Stream+Task+MERGE) |
| 4. JOIN | SELECT内で自由に記述 | 複数Streamの管理が必要 |
| 5. 増分ロジック | 自動判定 | 手動実装(きめ細かい制御可能) |
| 6. コスト | Full Refresh時に注意 | 常に増分のみ処理 |
| 7. 監視 | REFRESH_HISTORY | TASK_HISTORY |
Data Engineering
問題 1
あるチームは、受注テーブルの変更を検知してMERGEで集計テーブルを更新するパイプラインを運用している。要件として「毎日UTC 06:00に正確に実行すること」「DELETE行の変更前の値を使ってロールバック集計を行うこと」が求められている。最も適切な実装方式はどれか。
正解: C
「毎日UTC 06:00に正確に実行」というCRONベースの厳密なスケジュール要件と、「DELETE行の変更前後の値の追跡」という増分処理の細かい制御要件の2点は、いずれもDynamic Tablesでは対応できない領域です。Streams/TasksならCRON式でスケジュールを指定し、StreamのMETADATA$ACTIONとMETADATA$ISUPDATEでDELETE/UPDATE行を識別して処理できます。Materialized Viewは単一テーブルに対する集計に限られ、MERGE操作はサポートされません。
Dynamic TablesとStreams/Tasksは同じパイプライン内で併用できますか?
はい、併用可能です。たとえばDynamic Tablesで定義されたテーブルの変更をStreamで捕捉し、Task経由で外部テーブルやメール通知に連携するといった構成が取れます。ただし、Dynamic TableをStreamのソースにする場合はChange Tracking が自動的に有効になるため、追加設定は不要です。運用上は依存関係が複雑になりがちなので、DAGの可視化とアラート設計を事前に行うことが重要です。
Streams/TasksからDynamic Tablesへ移行するときの注意点は?
移行時に最も注意すべき点は、Streamのオフセット(消費位置)がDynamic Tablesには引き継がれないことです。Dynamic Tableは初回作成時にFull Refreshでデータを構築するため、移行期間中に二重処理が発生しないようカットオーバータイミングを計画する必要があります。また、Dynamic TablesではCRONベースの厳密な実行スケジュールは指定できず、TARGET_LAGによる最大遅延指定となるため、ジョブ実行順序に厳密な依存があるパイプラインでは設計の見直しが必要です。
コスト面ではどちらが有利ですか?
一概にはいえませんが、シンプルなETLパイプラインではDynamic Tablesの方がIncrementalリフレッシュの自動最適化により低コストになる傾向があります。一方、Streams/Tasksでは「変更がないときはTaskが発動しない」(WHEN SYSTEM$STREAM_HAS_DATA)という制御が可能なため、変更頻度が極端に低いテーブルではStreams/Tasksの方がクレジット消費を抑えられるケースがあります。いずれの場合もACCOUNT_USAGEのリフレッシュ履歴ビューで実際のクレジット消費を計測して判断することが重要です。
NicheeLab編集部
データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。
Snowflake資格一覧|全11試験(SnowPro)の難易度・費用
Snowflake認定資格(SnowPro)全11試験の一覧・難易度・費用・出題範囲を徹底解説。...
Snowflake試験の難易度ランキング|全11資格を徹底比較
Snowflake(SnowPro)認定全11試験の難易度をランキング形式で比較。学習時間・合格に必要なスキルから分析。...
Snowflake資格の勉強方法|効率的な学習ルートと合格のコツ
Snowflake認定資格(SnowPro)に最短で合格するための勉強方法。公式リソース・学習スケジュールを徹底ガイド。...
SnowPro Core試験完全解説|出題範囲・問題例・合格戦略
SnowPro Core Certification(COF-C03)を徹底解説。出題範囲・100問の試験形式・合格ライ...
SnowPro Platform Associate完全解説|入門試験の攻略
SnowPro Associate: Platform Certification(SOL-C01)を徹底解説。最も簡単...