第3章 データロード・アンロードと接続 / 想定学習時間:30〜40分 / 最終確認:2026年8月

3-4. StreamsとTasks(CDC・タスクグラフ・サーバーレスタスク)

🎯 この節の学習目標

1. Stream:テーブルの「変更」を追跡するオブジェクト

「昨夜の取り込み以降に変わった行だけを下流のテーブルに反映したい」——このような差分処理を実現するのが Stream(ストリーム)です。Stream は、対象テーブルに対する変更(挿入・更新・削除)を追跡するオブジェクトで、いわゆる CDC(Change Data Capture)を Snowflake の中で完結させます。

-- テーブルの変更を追跡するストリームを作成する
CREATE STREAM sales_stream ON TABLE raw_sales;

-- ストリームを SELECT すると「前回のオフセット以降の変更行」が見える
SELECT * FROM sales_stream;

Stream を SELECT すると、変更された行に加えて、変更の種類を示すメタデータ列が付いてきます。

メタデータ列意味
METADATA$ACTION変更の種類。INSERT または DELETE(更新は DELETE+INSERT のペアで表現される)
METADATA$ISUPDATEその行が UPDATE の一部かどうか(TRUE/FALSE)。更新なら DELETE 行・INSERT 行の両方が TRUE になる
METADATA$ROW_ID行を一意に識別する不変の ID。変更の前後で同じ行を対応付けられる

2. Stream の3つの種類

種類追跡する変更主な用途
standard(標準)挿入・更新・削除のすべて通常のテーブルのフル CDC
append-only挿入のみ(更新・削除は無視)追記中心のテーブルで、軽量に新規行だけを拾いたい場合
insert-only挿入のみ外部テーブル用(外部テーブルには standard を作れない)

📝 試験のポイント

「更新は METADATA$ACTION の DELETE と INSERT のペア+METADATA$ISUPDATE = TRUE で表現される」という点と、「insert-only ストリームは外部テーブル用」という対応付けが問われやすいポイントです。append-only(通常テーブルの挿入のみ)と insert-only(外部テーブル)の名前の違いにも注意しましょう。

3. オフセットの前進と stale 化

Stream は「どこまでの変更を読んだか」を示すオフセットを内部に持っています。重要なのはオフセットが進むタイミングです。

💡 具体例:ストリームの消費でオフセットが進む

-- 1回目:変更行が見える
SELECT COUNT(*) FROM sales_stream;   -- 例:120行

-- SELECT だけならオフセットは進まないので、もう一度見ても同じ
SELECT COUNT(*) FROM sales_stream;   -- 120行のまま

-- DML で消費するとオフセットが前進する
INSERT INTO sales_clean
SELECT sale_id, amount
FROM sales_stream
WHERE METADATA$ACTION = 'INSERT';

-- 消費後は空になる(新しい変更が来るまで)
SELECT COUNT(*) FROM sales_stream;   -- 0行

もう1つの注意点が stale(失効)化です。Stream の差分情報は、元テーブルのデータ保持期間(Time Travel の保持期間)に依存しています。Stream を長期間消費しないまま、オフセットが保持期間の外側に取り残されると、その Stream は stale となり、差分を読めなくなります。定期的に消費するか、保持期間を適切に設定することが防止策です(保持期間は 2-2 参照)。

4. Task:SQL をスケジュール実行する

Task(タスク)は、SQL 文(1つ)をスケジュールに従って自動実行するオブジェクトです。スケジュールの指定方法は2つあります。

-- 方法1:分間隔で指定する
CREATE TASK load_task
  WAREHOUSE = etl_wh
  SCHEDULE = '5 MINUTE'
  AS
  INSERT INTO sales_clean SELECT ... ;

-- 方法2:CRON 式で指定する(毎日午前2時、東京時間)
CREATE TASK nightly_task
  WAREHOUSE = etl_wh
  SCHEDULE = 'USING CRON 0 2 * * * Asia/Tokyo'
  AS
  CALL nightly_proc();

実行に使うコンピュートは2方式から選べます。

方式指定方法特徴
ユーザー管理ウェアハウスWAREHOUSE = ... を指定既存のウェアハウスで実行。サイズや同時実行を自分で管理する
サーバーレスタスクWAREHOUSE を指定せず、USER_TASK_MANAGED_INITIAL_WAREHOUSE_SIZE で初期サイズのヒントを与えるSnowflake がコンピュートのサイズを実行実績に応じて自動調整する。ウェアハウス管理が不要

また、タスクは作成した直後は suspended(停止)状態です。ALTER TASK ... RESUME を実行して初めてスケジュールどおりに動き始めます。デバッグなどで即時に1回だけ動かしたいときは EXECUTE TASK を使います。

5. タスクグラフ(DAG)と Stream との組み合わせ

複数のタスクは AFTER 句でつないでタスクグラフ(DAG)を構成できます。ルールは次のとおりです。

さらに、WHEN 句に SYSTEM$STREAM_HAS_DATA('stream名') を指定すると、「ストリームに未消費の変更があるときだけ」タスク本体を実行できます。変更がなければウェアハウスを起動せずにスキップするため、無駄なコンピュートコストを避けられます

-- ルートタスク:ストリームにデータがあるときだけ実行する
CREATE TASK merge_task
  WAREHOUSE = etl_wh
  SCHEDULE = '5 MINUTE'
  WHEN SYSTEM$STREAM_HAS_DATA('sales_stream')
  AS
  MERGE INTO sales_clean t
  USING sales_stream s ON t.sale_id = s.sale_id
  WHEN MATCHED AND s.METADATA$ACTION = 'DELETE' THEN DELETE
  WHEN MATCHED THEN UPDATE SET t.amount = s.amount
  WHEN NOT MATCHED THEN INSERT (sale_id, amount) VALUES (s.sale_id, s.amount);

-- 後続タスク:merge_task の完了後に集計を更新する
CREATE TASK agg_task
  WAREHOUSE = etl_wh
  AFTER merge_task
  AS
  INSERT OVERWRITE INTO sales_daily SELECT ... ;

-- 有効化:後続 → ルートの順に RESUME する
ALTER TASK agg_task RESUME;
ALTER TASK merge_task RESUME;
ソーステーブルへの変更INSERT / UPDATE / DELETE
Stream が差分(CDC)として捕捉
StreamMETADATA$ACTION / ISUPDATE / ROW_ID 付きで未消費の変更を保持
Task が WHEN SYSTEM$STREAM_HAS_DATA で起動判定
Task(ルート:MERGE で消費)DML 消費でオフセットが前進
AFTER で連結(タスクグラフ)
後続 Task → 変換先テーブル集計・整形などの下流処理

図:Stream と Task を組み合わせた差分パイプライン。変更があるときだけタスクが動く

✅ この節のまとめ

練習問題

問1. ソーステーブルのある行が UPDATE されたとき、standard ストリームにはどのように現れるか。

  1. METADATA$ACTION = 'UPDATE' の1行として現れる
  2. METADATA$ACTION が DELETE と INSERT の2行のペアとして現れ、両方の METADATA$ISUPDATE が TRUE になる
  3. 更新は追跡されず、ストリームには何も現れない
  4. METADATA$ROW_ID が新しい値に変わった1行として現れる
解答と解説を見る

正解:B

ストリームでは更新を「旧行の DELETE+新行の INSERT」のペアで表現し、両行の METADATA$ISUPDATE が TRUE になります。Aのような 'UPDATE' という METADATA$ACTION の値は存在しません。Cは append-only ストリームなら正しい説明ですが、standard ストリームは更新も追跡します。Dは誤りで、METADATA$ROW_ID は変更の前後で同じ行を対応付けるための不変の ID です。

問2. ストリームのオフセットが前進する(消費される)のはどの操作か。

  1. ストリームに対して SELECT を実行したとき
  2. ストリームをソースとする DML(INSERT ... SELECT や MERGE)を含むトランザクションが成功したとき
  3. ソーステーブルに新しい行が挿入されたとき
  4. SHOW STREAMS を実行したとき
解答と解説を見る

正解:B

オフセットが進むのは、ストリームを DML のソースとして消費したトランザクションが成功したときです。Aの SELECT は何度実行しても同じ変更セットが見えるだけで、オフセットは進みません。Cはストリームに新しい変更が「追加」されるだけです。Dはメタデータの表示コマンドで、消費とは無関係です。

問3. タスクに関する説明として正しいものはどれか(2つ選べ)。

  1. タスクは作成直後から自動的にスケジュール実行が始まる
  2. タスクグラフでスケジュールを設定できるのはルートタスクだけで、後続タスクは AFTER で連結する
  3. WAREHOUSE を指定しないサーバーレスタスクでは、Snowflake がコンピュートサイズを自動調整する
  4. タスクを手動で即時実行する方法は存在しない
解答と解説を見る

正解:B・C

タスクグラフではルートタスクのみが SCHEDULE を持ち、後続は AFTER で先行タスクに連結します。また WAREHOUSE を指定しなければサーバーレスタスクとなり、USER_TASK_MANAGED_INITIAL_WAREHOUSE_SIZE を初期ヒントに Snowflake がサイズを自動調整します。Aは誤りで、作成直後は suspended 状態のため ALTER TASK ... RESUME が必要です。Dも誤りで、EXECUTE TASK により手動で即時実行できます。

問4. 「ストリームに未消費の変更があるときだけタスクを実行し、変更がなければウェアハウスを起動せずスキップしたい」。WHEN 句に指定すべきものはどれか。

  1. SYSTEM$PIPE_STATUS('sales_stream')
  2. SYSTEM$STREAM_HAS_DATA('sales_stream')
  3. VALIDATE(sales_stream, JOB_ID => '_last')
  4. METADATA$ACTION = 'INSERT'
解答と解説を見る

正解:B

SYSTEM$STREAM_HAS_DATA はストリームに未消費の変更があるかを返す関数で、タスクの WHEN 句に指定すると変更がないときは本体を実行せずスキップし、コンピュートコストを節約できます。Aはパイプ(Snowpipe)の状態確認用です。Cは COPY のロードエラーを後から確認するテーブル関数です。Dはストリームを SELECT する際のフィルタ条件に使う列であり、タスクの起動判定関数ではありません。