第3章 データロード・アンロードと接続(Loading, Unloading & Connectivity, 18%)
🎯 この節の学習目標
「昨夜の取り込み以降に変わった行だけを下流のテーブルに反映したい」——このような差分処理を実現するのが 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。変更の前後で同じ行を対応付けられる |
| 種類 | 追跡する変更 | 主な用途 |
|---|---|---|
| standard(標準) | 挿入・更新・削除のすべて | 通常のテーブルのフル CDC |
| append-only | 挿入のみ(更新・削除は無視) | 追記中心のテーブルで、軽量に新規行だけを拾いたい場合 |
| insert-only | 挿入のみ | 外部テーブル用(外部テーブルには standard を作れない) |
📝 試験のポイント
「更新は METADATA$ACTION の DELETE と INSERT のペア+METADATA$ISUPDATE = TRUE で表現される」という点と、「insert-only ストリームは外部テーブル用」という対応付けが問われやすいポイントです。append-only(通常テーブルの挿入のみ)と insert-only(外部テーブル)の名前の違いにも注意しましょう。
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 参照)。
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 を使います。
複数のタスクは AFTER 句でつないでタスクグラフ(DAG)を構成できます。ルールは次のとおりです。
AFTER 先行タスク名 で連結し、先行タスクの完了を受けて実行されます。SYSTEM$TASK_DEPENDENTS_ENABLE もあります)。さらに、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;
図:Stream と Task を組み合わせた差分パイプライン。変更があるときだけタスクが動く
✅ この節のまとめ
問1. ソーステーブルのある行が UPDATE されたとき、standard ストリームにはどのように現れるか。
正解:B
ストリームでは更新を「旧行の DELETE+新行の INSERT」のペアで表現し、両行の METADATA$ISUPDATE が TRUE になります。Aのような 'UPDATE' という METADATA$ACTION の値は存在しません。Cは append-only ストリームなら正しい説明ですが、standard ストリームは更新も追跡します。Dは誤りで、METADATA$ROW_ID は変更の前後で同じ行を対応付けるための不変の ID です。
問2. ストリームのオフセットが前進する(消費される)のはどの操作か。
正解:B
オフセットが進むのは、ストリームを DML のソースとして消費したトランザクションが成功したときです。Aの SELECT は何度実行しても同じ変更セットが見えるだけで、オフセットは進みません。Cはストリームに新しい変更が「追加」されるだけです。Dはメタデータの表示コマンドで、消費とは無関係です。
問3. タスクに関する説明として正しいものはどれか(2つ選べ)。
正解:B・C
タスクグラフではルートタスクのみが SCHEDULE を持ち、後続は AFTER で先行タスクに連結します。また WAREHOUSE を指定しなければサーバーレスタスクとなり、USER_TASK_MANAGED_INITIAL_WAREHOUSE_SIZE を初期ヒントに Snowflake がサイズを自動調整します。Aは誤りで、作成直後は suspended 状態のため ALTER TASK ... RESUME が必要です。Dも誤りで、EXECUTE TASK により手動で即時実行できます。
問4. 「ストリームに未消費の変更があるときだけタスクを実行し、変更がなければウェアハウスを起動せずスキップしたい」。WHEN 句に指定すべきものはどれか。
正解:B
SYSTEM$STREAM_HAS_DATA はストリームに未消費の変更があるかを返す関数で、タスクの WHEN 句に指定すると変更がないときは本体を実行せずスキップし、コンピュートコストを節約できます。Aはパイプ(Snowpipe)の状態確認用です。Cは COPY のロードエラーを後から確認するテーブル関数です。Dはストリームを SELECT する際のフィルタ条件に使う列であり、タスクの起動判定関数ではありません。