BigQuery の APPENDS / CHANGES 関数でテーブルの変更履歴を追ってみた

BigQuery の APPENDS / CHANGES 関数でテーブルの変更履歴を追ってみた

BigQuery の変更履歴関数 `APPENDS` / `CHANGES` が GA になりました。タスク管理テーブルで INSERT → UPDATE → DELETE を実行し、両方の関数を試してみたので、その結果をご紹介します。
2026.08.29

こんにちは!エノカワです。

BigQuery で「このテーブル、さっきまでどんな中身だったか」を確認したいとき、よく使うのが Time travel です。
FOR SYSTEM_TIME AS OF で過去のある時点のテーブル全体を参照できるので、誤って UPDATE / DELETE してしまったデータの復旧にはとても便利です。

一方で、当時の全件ではなく、その期間に変化した行だけ を追いたいこともあります。

2026 年 7 月 27 日、その用途向けの APPENDS / CHANGES 変更履歴関数 が GA となりました。

You can use the APPENDS and CHANGES change history functions to view the rows that were appended to or changed in a table during a given time range. This feature is generally available (GA).

SQL でテーブルの変更行を読む関数です。
履歴の確認にも使えますし、その結果を増分処理の材料にもできます。
テーブルが更新されたタイミングでジョブを自動起動する機能ではない点は、最初に押さえておくとよさそうです。

今回は、日本語のタスク管理データを使って、INSERT → UPDATE → DELETE のあとに両方の関数を試してみましたので、その内容をご紹介します。

APPENDS と CHANGES の違い

Time travel が「ある時点のテーブル全体」なのに対して、こちらは「指定期間に起きた変更行」です。
公式ドキュメントの内容を対比すると次のとおりです。

観点 Time travel APPENDS CHANGES
見えるもの ある時点のテーブル全体 期間内に追加された行 期間内に追加・更新・削除された行
_CHANGE_TYPE なし INSERT のみ INSERT / UPDATE / DELETE
事前設定 不要 不要 テーブルenable_change_history=TRUE
保持期間 time travel 窓(標準 7 日) 同左 同左

入力テーブルの列に加えて、次の列が付きます。

  • _CHANGE_TYPE … 変更の種類
  • _CHANGE_TIMESTAMP … その変更をコミットした時刻
  • _CHANGE_IS_FOR_UPDATECHANGES のみ。更新由来の DELETE なら TRUE

enable_change_history はプロジェクトやデータセットではなく、テーブル単位 の OPTIONS です。
CREATE TABLEALTER TABLE ... SET OPTIONS で指定します。
Time travel の保持期間はデータセット設定(2〜7 日)なので、「何日残すか」と「CHANGES 用メタデータを取るか」は別レイヤでした。

構文は次のとおりです。
TABLE キーワードは必須です。
end_timestamp は排他で、NULL にするとクエリ開始時点までが含まれます。

SELECT *
FROM APPENDS(TABLE dataset.table, start_timestamp, end_timestamp);

SELECT *
FROM CHANGES(TABLE dataset.table, start_timestamp, end_timestamp);

今回やること

小さなタスク管理テーブルを作り、次の順で操作します。

  1. タスクを 3 件 INSERT する
  2. 1 件のステータスを UPDATE する
  3. 1 件を DELETE する
  4. 同じテーブルに対して APPENDSCHANGES を実行し、結果を比べる

環境準備

BigQuery でデータセットを作成できる権限があれば十分です。
変更履歴の参照には bigquery.tables.getDataroles/bigquery.dataViewer など)が必要です。

検証用のデータセットを作成します。

CREATE SCHEMA IF NOT EXISTS change_history_demo;

CHANGES を使うので、テーブル作成時から enable_change_history=TRUE にしました。
後から有効化すると、有効化前の履歴が不完全になることがあるためです。

CREATE TABLE change_history_demo.tasks (
  task_id INT64,
  title STRING,
  status STRING,
  assignee STRING
)
OPTIONS(enable_change_history = TRUE);

作成後、コンソールの 詳細 タブと スキーマ / スキーマを編集 を見たのですが、enable_change_history は出てきませんでした。
有効かどうかは INFORMATION_SCHEMA.TABLESis_change_history_enabled で確認できました。

SELECT
  table_name,
  is_change_history_enabled
FROM
  `change_history_demo.INFORMATION_SCHEMA.TABLES`
WHERE
  table_name = 'tasks';

今回の結果は次のとおりでした。

table_name is_change_history_enabled
tasks YES

bigquery-appends-changes-change-history_01.png

APPENDS だけならこのオプションは不要です。
今回は両方を同じテーブルで試すため、最初から有効にしています。

データを投入・更新・削除する

まず 3 件のタスクを追加します。

INSERT INTO change_history_demo.tasks (task_id, title, status, assignee)
VALUES
  (1, '請求書の確認', '未着手', '山田'),
  (2, '週次レポート作成', '未着手', '佐藤'),
  (3, '障害対応の振り返り', '進行中', '鈴木');

次に、タスク 1 のステータスを更新します。

UPDATE change_history_demo.tasks
SET status = '完了'
WHERE task_id = 1;

最後に、タスク 2 を削除します。

DELETE FROM change_history_demo.tasks
WHERE task_id = 2;

現在のテーブルは次のようになりました。
削除した「週次レポート作成」は見えず、タスク 1 は完了済みです。

SELECT * FROM change_history_demo.tasks
ORDER BY task_id;
task_id title status assignee
1 請求書の確認 完了 山田
3 障害対応の振り返り 進行中 鈴木

bigquery-appends-changes-change-history_02.png

ここからが本題です。
同じ操作のあと、APPENDSCHANGES でどう見えるかを比べます。

APPENDS を試す

追加履歴だけを見ます。
テーブル作成直後なので、time travel 窓内の全件を取る意図で NULL, NULL を指定しました。

SELECT
  task_id,
  title,
  status,
  assignee,
  _CHANGE_TYPE AS change_type,
  _CHANGE_TIMESTAMP AS change_time
FROM APPENDS(TABLE change_history_demo.tasks, NULL, NULL)
ORDER BY change_time, task_id;

結果は次のとおりでした。
_CHANGE_TYPE はすべて INSERT です。

task_id title status assignee change_type change_time
1 請求書の確認 未着手 山田 INSERT 2026-08-28 09:21:22.345000 UTC
2 週次レポート作成 未着手 佐藤 INSERT 2026-08-28 09:21:22.345000 UTC
3 障害対応の振り返り 進行中 鈴木 INSERT 2026-08-28 09:21:22.345000 UTC

bigquery-appends-changes-change-history_03.png

やってみて分かったのは、次の 2 点です。

  • タスク 1 の statusINSERT 当時の「未着手」 のままです。その後の UPDATE は APPENDS に出ませんでした
  • すでに DELETE したタスク 2 も、当時 INSERT された行として残っていました

「期間内に何が追加されたか」は取れますが、更新後の値や削除そのものは見えませんでした。

CHANGES を試す

同じテーブルに対して CHANGES を実行します。

SELECT
  task_id,
  title,
  status,
  assignee,
  _CHANGE_TYPE AS change_type,
  _CHANGE_TIMESTAMP AS change_time,
  _CHANGE_IS_FOR_UPDATE AS change_is_for_update
FROM CHANGES(TABLE change_history_demo.tasks, NULL, NULL)
ORDER BY change_time, task_id, change_type;

結果は次のとおりでした。
UPDATE は「旧行の DELETE + 新行の UPDATE」として 2 行出てきました。

task_id title status assignee change_type change_time change_is_for_update
1 請求書の確認 未着手 山田 INSERT 2026-08-28 09:21:22.345000 UTC false
2 週次レポート作成 未着手 佐藤 INSERT 2026-08-28 09:21:22.345000 UTC false
3 障害対応の振り返り 進行中 鈴木 INSERT 2026-08-28 09:21:22.345000 UTC false
1 請求書の確認 未着手 山田 DELETE 2026-08-28 09:22:24.295000 UTC true
1 請求書の確認 完了 山田 UPDATE 2026-08-28 09:22:24.295000 UTC false
2 週次レポート作成 未着手 佐藤 DELETE 2026-08-28 09:25:07.016000 UTC false

bigquery-appends-changes-change-history_04.png

行を更新すると、次の 2 レコードが並びます。

  • 更新前の行 … _CHANGE_TYPE = DELETE かつ _CHANGE_IS_FOR_UPDATE = TRUE
  • 更新後の行 … _CHANGE_TYPE = UPDATE

本物の DELETE(タスク 2)は _CHANGE_IS_FOR_UPDATE = FALSE でした。
「消えたように見える行」が、本当の削除なのか UPDATE の片割れなのかは、このフラグで区別できました。

使い分けの指針

両方を同じテーブルで動かしてみて、自分なりに整理した使い分けは次のとおりです。

  • 追加された行だけを拾いたい(ログの取り込み、追記型テーブルの増分同期)→ APPENDS。事前設定なしで、既存テーブルにもそのまま使える
  • 更新・削除まで追いたい(マスタ系の増分同期、「いつどの行が変わったか」の調査)→ CHANGES。ただしテーブル単位の事前設定が必要
  • ある時点の全体を見たい(誤操作からの復旧、当時の件数の確認)→ 従来どおり time travel

CHANGES は後から有効化しても、有効化前の履歴はさかのぼれません。
更新・削除も追う可能性があるテーブルは、作成時点で enable_change_history=TRUE にしておくほうが安全だと感じました。

まとめ

以上、GA になった BigQuery の変更履歴関数 APPENDS / CHANGES を、小さなタスク管理テーブルで試してみました。

実際に動かしてみると、Time travel との違いは「全体を見るか、変わった行を見るか」で整理でき、思っていたよりシンプルでした。
APPENDS は追加された行だけが、INSERT 当時の値のまま残ります。
CHANGES は更新前後の行が並んで返ってくるので、「いつどの行がどう変わったか」を SQL だけで追えるのが便利だと感じました。
一方で、CHANGES はテーブル単位で enable_change_history=TRUE にしておく必要があり、後からではさかのぼれません。
指定できる期間は最大 1 日で、保持も time travel 窓(標準 7 日)に依存します。
関数呼び出しは指定期間の書き込みを処理するため、コンピュート課金も念頭に置いておくとよさそうです。

テーブルの変更を行単位で追いたい方は、ぜひ触ってみてはいかがでしょうか?

参考

この記事をシェアする

関連記事