概要
Snowflake の CHANGES 句(STREAM)と Databricks の CHANGES 句の動作差異について調査した内容を共有します。確認した差異は下記のとおりです。これらのサービス間で移行を行う際には、動作差異を踏まえたうえで作業を進める必要があります。
- Snowflake と Databricks では、UPDATE 時の記録方法が異なること
- Snowflake では複数の DML 操作が集約された単一レコードとして記録される一方で、Databricks では DML の処理内容を複数レコードとして取得できること
- UPDATE と DELETE が同時に行われた場合、Snowflake では UPDATE 前の値に対して DELETE された形で記録される一方で、Databricks ではそれぞれの DML 操作を取得できること
- INSERT と DELETE が同時に行われた場合、Snowflake ではそのレコードを取得できない一方で、Databricks ではそれぞれの DML 操作を取得できること
仕様差異
変更内容のメタデータ列の基本的な仕様
Snowflake と Databricks で CHANGES 句を利用した際に追加されるカラムは下記のとおりです。
出所: Introduction to streams | Snowflake Documentation
出所: Azure Databricks で Delta Lake の変更データ フィードを使用する - Azure Databricks | Microsoft Learn
UPDATE の仕様差異
UPDATE 時の取得方法が異なる点には注意が必要です。Snowflake では METADATA$ACTION が INSERT または DELETE であり、かつ METADATA$ISUPDATE が TRUE の条件で取得できます。一方、Databricks では _change_type 列が update_preimage および update_postimage の条件で取得できます。名称が異なる点にも注意が必要です。
動作再現
INSERT と UPDATE の動作確認
ID が 1 のレコードに対して、INSERT(val を A) -> UPDATE(val を B) -> UPDATE(val を C)の処理を実施した結果が下記です。Snowflake では val が C のみのレコードが表示されていることから、最終処理後のデータが取得できることを確認できます。一方、Databricks ではそれぞれの DML 操作に対応するレコードを取得できています。
Snowflake の結果
| ID | VAL | TS | ACTION | IS_UPDATE | ROW_ID |
|---|---|---|---|---|---|
| 1 | C | 2026-04-08 19:27:47.408 | INSERT | FALSE | cf574746f7d568aeb11338364d82b600fab2a701 |
Databricks の結果
| id | val | ts | _change_type | _commit_version | _commit_timestamp |
|---|---|---|---|---|---|
| 1 | A | 2026-04-09T02:33:22.194+00:00 | insert | 1 | 2026-04-09T02:33:23.000+00:00 |
| 1 | B | 2026-04-09T02:33:23.357+00:00 | update_postimage | 2 | 2026-04-09T02:33:27.000+00:00 |
| 1 | A | 2026-04-09T02:33:22.194+00:00 | update_preimage | 2 | 2026-04-09T02:33:27.000+00:00 |
| 1 | C | 2026-04-09T02:33:27.244+00:00 | update_postimage | 3 | 2026-04-09T02:33:30.000+00:00 |
| 1 | B | 2026-04-09T02:33:23.357+00:00 | update_preimage | 3 | 2026-04-09T02:33:30.000+00:00 |
UPDATE と DELETE の動作確認
ID が 1 のレコードに対して、UPDATE(val を D) -> DELETE の処理を実施した結果が下記です。Snowflake では val が C のレコードのみが表示されていることから、UPDATE 前のレコードに対して DELETE されたデータが取得できることを確認できます。一方、Databricks ではそれぞれの DML 操作に対応するレコードを取得できています。UPDATE 後の値である D のレコードを取得できることを想定していたためSnowflake のこの動作は想定と異なる結果でしたが、前回からの差分と捉えればCのデータを削除したという解釈ができます。
Snowflake の結果
| ID | VAL | ACTION | IS_UPDATE | ROW_ID |
|---|---|---|---|---|
| 1 | C | DELETE | FALSE | cf574746f7d568aeb11338364d82b600fab2a701 |
Databricks の結果
| id | val | ts | _change_type | _commit_version | _commit_timestamp |
|---|---|---|---|---|---|
| 1 | D | 2026-04-09T02:33:27.244+00:00 | update_postimage | 4 | 2026-04-09T02:33:39.000+00:00 |
| 1 | C | 2026-04-09T02:33:27.244+00:00 | update_preimage | 4 | 2026-04-09T02:33:39.000+00:00 |
| 1 | D | 2026-04-09T02:33:27.244+00:00 | delete | 5 | 2026-04-09T02:33:41.000+00:00 |
INSERT と DELETE の動作確認
ID が 2 のレコードに対して、INSERT(val を Z) -> DELETE の処理を実施した結果が下記です。Snowflake ではデータを取得できない一方で、Databricks ではそれぞれの DML 操作に対応するレコードを取得できています。
Snowflake の結果
データなし。
Databricks の結果
| id | val | ts | _change_type | _commit_version | _commit_timestamp |
|---|---|---|---|---|---|
| 2 | Z | 2026-04-09T02:33:47.363+00:00 | insert | 6 | 2026-04-09T02:33:48.000+00:00 |
| 2 | Z | 2026-04-09T02:33:47.363+00:00 | delete | 7 | 2026-04-09T02:33:50.000+00:00 |
検証コード
Snowflake
事前準備
CREATE DATABASE IF NOT EXISTS changes_test;
CREATE SCHEMA IF NOT EXISTS changes_test.schema_01;
USE DATABASE changes_test;
USE SCHEMA changes_test.schema_01;
-- Snowflake SQL
DROP TABLE IF EXISTS change_verify_sf;
CREATE TABLE change_verify_sf (
id NUMBER,
val STRING,
ts timestamp
);
ALTER TABLE change_verify_sf SET CHANGE_TRACKING = TRUE;
-- ここで標準 stream を作成
CREATE OR REPLACE STREAM change_verify_sf_s
ON TABLE change_verify_sf;
INSERT と UPDATE の動作確認
-- ここを区間開始点にする
SET ts_start = (SELECT CURRENT_TIMESTAMP());
INSERT INTO change_verify_sf VALUES (1, 'A', current_timestamp());
UPDATE change_verify_sf
SET val = 'B',ts = current_timestamp()
WHERE id = 1;
UPDATE change_verify_sf
SET val = 'C',ts = current_timestamp()
WHERE id = 1;
SELECT
id,
val,
ts,
METADATA$ACTION AS action,
METADATA$ISUPDATE AS is_update,
METADATA$ROW_ID AS row_id
FROM change_verify_sf
CHANGES(INFORMATION => DEFAULT)
AT(TIMESTAMP => $ts_start)
ORDER BY id, action;
| ID | VAL | TS | ACTION | IS_UPDATE | ROW_ID |
|---|---|---|---|---|---|
| 1 | C | 2026-04-08 19:27:47.408 | INSERT | FALSE | cf574746f7d568aeb11338364d82b600fab2a701 |
SELECT * FROM change_verify_sf_s;
| ID | VAL | TS | METADATA$ACTION | METADATA$ISUPDATE | METADATA$ROW_ID |
|---|---|---|---|---|---|
| 1 | C | 2026-04-08 19:27:47.408 | INSERT | FALSE | cf574746f7d568aeb11338364d82b600fab2a701 |
-- STREAM を消費
CREATE OR REPLACE TEMP TABLE temp_test_01
AS
SELECT * FROM change_verify_sf_s;
UPDATE と DELETE の動作確認
-- DELETE 前の状態を取得
SET ts_delete_pre = (SELECT CURRENT_TIMESTAMP());
UPDATE change_verify_sf
SET val = 'D',ts = current_timestamp()
WHERE id = 1;
-- DELETE 直後の状態を取得
SET ts_delete_post = (SELECT CURRENT_TIMESTAMP());
DELETE FROM change_verify_sf
WHERE id = 1;
SELECT
id,
val,
METADATA$ACTION AS action,
METADATA$ISUPDATE AS is_update,
METADATA$ROW_ID AS row_id
FROM change_verify_sf
CHANGES(INFORMATION => DEFAULT)
AT(TIMESTAMP => $ts_delete_pre)
ORDER BY id, action;
| ID | VAL | ACTION | IS_UPDATE | ROW_ID |
|---|---|---|---|---|
| 1 | C | DELETE | FALSE | cf574746f7d568aeb11338364d82b600fab2a701 |
SELECT * FROM change_verify_sf_s;
| ID | VAL | TS | METADATA$ACTION | METADATA$ISUPDATE | METADATA$ROW_ID |
|---|---|---|---|---|---|
| 1 | C | 2026-04-08 19:27:47.408 | DELETE | FALSE | cf574746f7d568aeb11338364d82b600fab2a701 |
DELETE 直後の CHANGES 句の結果は下記です。途中で STREAM で確認した結果と一致します。
SELECT
id,
val,
METADATA$ACTION AS action,
METADATA$ISUPDATE AS is_update,
METADATA$ROW_ID AS row_id
FROM change_verify_sf
CHANGES(INFORMATION => DEFAULT)
AT(TIMESTAMP => $ts_delete_post)
ORDER BY id, action;
| ID | VAL | ACTION | IS_UPDATE | ROW_ID |
|---|---|---|---|---|
| 1 | D | DELETE | FALSE | cf574746f7d568aeb11338364d82b600fab2a701 |
-- STREAM を消費
CREATE OR REPLACE TEMP TABLE temp_test_02
AS
SELECT * FROM change_verify_sf_s;
INSERT と DELETE の動作確認
-- INSERT と DELETE の時点を取得
SET ts_insert_and_delete = (SELECT CURRENT_TIMESTAMP());
INSERT INTO change_verify_sf VALUES (2, 'Z', current_timestamp());
DELETE FROM change_verify_sf
WHERE id = 2;
SELECT
id,
val,
METADATA$ACTION AS action,
METADATA$ISUPDATE AS is_update,
METADATA$ROW_ID AS row_id
FROM change_verify_sf
CHANGES(INFORMATION => DEFAULT)
AT(TIMESTAMP => $ts_insert_and_delete)
ORDER BY id, action;
SELECT * FROM change_verify_sf_s;
Databricks
事前準備
%sql
CREATE CATALOG IF NOT EXISTS changes_test;
CREATE SCHEMA IF NOT EXISTS changes_test.schema_01;
%sql
USE CATALOG changes_test;
USE changes_test.schema_01;
%sql
-- Databricks SQL
DROP TABLE IF EXISTS change_verify_dbx;
CREATE TABLE change_verify_dbx
(
id INT,
val STRING,
ts TIMESTAMP
)
TBLPROPERTIES (delta.enableChangeDataFeed = true);
INSERT と UPDATE の動作確認
%sql
-- version 1
INSERT INTO change_verify_dbx VALUES (1, 'A', current_timestamp());
-- version 2
UPDATE change_verify_dbx
SET val = 'B',ts = current_timestamp()
WHERE id = 1;
-- version 3
UPDATE change_verify_dbx
SET val = 'C',ts = current_timestamp()
WHERE id = 1;
%sql
SELECT
id,
val,
ts,
_change_type,
_commit_version,
_commit_timestamp
FROM table_changes('change_verify_dbx', 1)
ORDER BY _commit_version, id, _change_type;
| id | val | ts | _change_type | _commit_version | _commit_timestamp |
|---|---|---|---|---|---|
| 1 | A | 2026-04-09T02:33:22.194+00:00 | insert | 1 | 2026-04-09T02:33:23.000+00:00 |
| 1 | B | 2026-04-09T02:33:23.357+00:00 | update_postimage | 2 | 2026-04-09T02:33:27.000+00:00 |
| 1 | A | 2026-04-09T02:33:22.194+00:00 | update_preimage | 2 | 2026-04-09T02:33:27.000+00:00 |
| 1 | C | 2026-04-09T02:33:27.244+00:00 | update_postimage | 3 | 2026-04-09T02:33:30.000+00:00 |
| 1 | B | 2026-04-09T02:33:23.357+00:00 | update_preimage | 3 | 2026-04-09T02:33:30.000+00:00 |
UPDATE と DELETE の動作確認
%sql
-- version 4
UPDATE change_verify_dbx
SET val = 'D'
WHERE id = 1;
-- version 5
DELETE FROM change_verify_dbx
WHERE id = 1;
%sql
SELECT
id,
val,
ts,
_change_type,
_commit_version,
_commit_timestamp
FROM table_changes('change_verify_dbx', 4)
ORDER BY _commit_version, id, _change_type;
| id | val | ts | _change_type | _commit_version | _commit_timestamp |
|---|---|---|---|---|---|
| 1 | D | 2026-04-09T02:33:27.244+00:00 | update_postimage | 4 | 2026-04-09T02:33:39.000+00:00 |
| 1 | C | 2026-04-09T02:33:27.244+00:00 | update_preimage | 4 | 2026-04-09T02:33:39.000+00:00 |
| 1 | D | 2026-04-09T02:33:27.244+00:00 | delete | 5 | 2026-04-09T02:33:41.000+00:00 |
INSERT と DELETE の動作確認
%sql
-- version 6
INSERT INTO change_verify_dbx VALUES (2, 'Z', current_timestamp());
-- version 7
DELETE FROM change_verify_dbx
WHERE id = 2;
%sql
SELECT
id,
val,
ts,
_change_type,
_commit_version,
_commit_timestamp
FROM table_changes('change_verify_dbx', 6)
ORDER BY _commit_version, id, _change_type;
| id | val | ts | _change_type | _commit_version | _commit_timestamp |
|---|---|---|---|---|---|
| 2 | Z | 2026-04-09T02:33:47.363+00:00 | insert | 6 | 2026-04-09T02:33:48.000+00:00 |
| 2 | Z | 2026-04-09T02:33:47.363+00:00 | delete | 7 | 2026-04-09T02:33:50.000+00:00 |






















