はじめに:うっかりDELETEや上書きをやってしまったとき

データ基盤を運用していると、誰しも一度は冷や汗をかく瞬間があります。

  • WHERE を付け忘れたまま DELETE を実行して、テーブルの中身が全部消えた
  • 検証のつもりで叩いた UPDATE が本番テーブルに当たって、値が上書きされた
  • スケジュールクエリの CREATE OR REPLACE TABLE が想定外のデータで走り、正しい集計が消えた

こうした「うっかり」は、どれだけ気をつけていても完全にはなくせません。特に中小ECや個人事業の現場では、一人で複数の作業を兼任していることが多く、確認の余裕がないまま操作してしまいがちです。

BigQuery には、こうした事故からデータを取り戻すための仕組みが標準で備わっています。それが タイムトラベル(Time Travel) です。この記事では、タイムトラベルとは何か、過去の状態を参照して復旧する具体的な手順、そして「これだけでは守りきれない範囲」について整理します。

なお、保持期間などの仕様は変更される可能性があるため、実際に運用へ組み込む前に最新の公式ドキュメントで確認してください。

タイムトラベルとは(既定で過去7日間をさかのぼれる)

タイムトラベルは、BigQuery がテーブルの過去の状態を一定期間保持してくれる機能です。明示的にバックアップを取っていなくても、一定期間内であれば「少し前のテーブルの中身」を参照できます。

ポイントは次のとおりです。

  • 既定の保持期間は過去7日間 — 何も設定しなくても、直近の一定期間をさかのぼれます。保持期間はデータセット単位で調整できる場合があるため、運用方針に合わせて確認してください。
  • 追加の設定が不要 — バックアップ用のジョブを組まなくても、機能として最初から有効になっています。
  • 誤操作の直後ほど有効 — 削除や上書きの「直前」の状態を指定して参照できるため、事故に気づいたらすぐ動くのが鉄則です。

つまりタイムトラベルは「短期の保険」です。長期の保管を目的としたものではない、という点が後ほど重要になります。

なぜ「すぐ動く」ことが大事なのか

タイムトラベルで戻れるのは、あくまで保持期間の範囲内に限られます。事故から時間が経つほど、戻したい時点が保持期間の外へ押し出されていきます。

加えて、事故に気づかないまま新しい誤操作を重ねてしまうと、状況の切り分けが難しくなります。「どの時点が正しい状態だったのか」が分からなくなると、復旧の判断そのものが難しくなるためです。

ですから、データの異常に気づいたら、まずは追加の書き込みをいったん止めることが重要です。スケジュールクエリが動いている場合は、復旧が済むまで一時停止しておくと、状況がそれ以上悪化しません。落ち着いて過去の状態を確認できる環境を整えてから、復旧作業に入りましょう。

FOR SYSTEM_TIME AS OF で過去の状態を参照する

過去の状態を参照するには、FOR SYSTEM_TIME AS OF という構文を使います。テーブル名の直後に「いつ時点の状態を見たいか」を指定するだけです。

まず壊れる前の状態を確認する

事故に気づいたら、いきなり書き戻すのではなく、まずは過去の状態が本当に残っているかを確認します。たとえば「1時間前」の状態を見たい場合は次のように書きます。

SELECT *
FROM `myproject.mydataset.orders`
  FOR SYSTEM_TIME AS OF TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR);

件数が想定どおりかを確認します。

SELECT COUNT(*) AS row_count
FROM `myproject.mydataset.orders`
  FOR SYSTEM_TIME AS OF TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR);

誤操作の正確な時刻が分かっている場合は、その「直前」を絶対時刻で指定するほうが確実です。

SELECT *
FROM `myproject.mydataset.orders`
  FOR SYSTEM_TIME AS OF TIMESTAMP("2026-07-30 09:14:00+09:00");

別テーブルに退避してから戻す

過去の状態が問題なく取れることを確認できたら、復旧に進みます。ここでいきなり本番テーブルへ書き戻すのは危険なので、まずは検証用の別テーブルに退避させます。

CREATE TABLE `myproject.mydataset.orders_recovered` AS
SELECT *
FROM `myproject.mydataset.orders`
  FOR SYSTEM_TIME AS OF TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR);

退避したテーブルの中身を目視で確認し、本当に正しい状態かを納得できたら、本番テーブルへ反映します。全件が消えていたケースなら、まるごと作り直すのが分かりやすいです。

CREATE OR REPLACE TABLE `myproject.mydataset.orders` AS
SELECT *
FROM `myproject.mydataset.orders_recovered`;

一部の行だけを戻したい場合は、MERGE を使って差分だけを当てる方法もあります。たとえば、誤って一部の注文金額だけが書き換わったようなケースでは、退避テーブルを正として該当行だけを更新します。

MERGE `myproject.mydataset.orders` AS target
USING `myproject.mydataset.orders_recovered` AS source
ON target.order_id = source.order_id
WHEN MATCHED THEN
  UPDATE SET target.amount = source.amount;

いずれにせよ、本番テーブルへ書き込む操作の前には、もう一度件数や主要な値を確認する習慣をつけておくと安全です。書き戻したあとも、復旧後のテーブルが期待どおりになっているかをすぐに確認します。

復旧後に退避テーブルを片付ける

復旧が完了し、本番テーブルが正常だと確認できたら、検証用に作った退避テーブルやクローンは整理しておきます。残したままにすると、後から見たときにどれが本番か分からなくなりますし、ストレージ費用も無駄になります。ただし、削除はあくまで「本番が正しいと確信できてから」にしてください。慌てて消すと、戻し先を失うことになりかねません。

スナップショットとCLONEを併用する

タイムトラベルは便利ですが、あくまで保持期間内の自動的な保険です。これと組み合わせて使うと心強いのが、テーブルスナップショットとテーブルクローンです。

テーブルスナップショット

テーブルスナップショットは、ある時点のテーブルを読み取り専用で固定保存する機能です。タイムトラベルの保持期間が切れてしまう前に、節目の状態をスナップショットとして残しておけば、より長く参照できます。

CREATE SNAPSHOT TABLE `myproject.mydataset.orders_snap_20260730`
CLONE `myproject.mydataset.orders`;

スナップショットは元テーブルとストレージを共有するため、丸ごとコピーするよりストレージ費用を抑えやすいのが利点です。重要な更新処理の前に1本取っておくと、いざというときの戻し先になります。

テーブルクローン

テーブルクローンは、ある時点のテーブルを「書き込み可能な独立したテーブル」として複製する機能です。スナップショットが読み取り専用なのに対し、クローンは検証用に自由に書き換えられます。

CREATE TABLE `myproject.mydataset.orders_clone`
CLONE `myproject.mydataset.orders`;

「本番に近いデータで試したいが、本番は壊したくない」という場面で役立ちます。スナップショットとタイムトラベルを組み合わせれば、過去の任意時点をクローンとして起こすことも可能です。

なお、スナップショットやクローンの保存期限・費用の扱いは変わることがあるため、運用に組み込む際は最新の公式ドキュメントで確認してください。

⚠️ 保持期間には限界がある。恒久バックアップは別途用意する

ここが一番大事な注意点です。タイムトラベルはあくまで短期の保険であり、保持期間を過ぎた状態は参照できません。

  • タイムトラベル単体では長期保管にならない — 既定の保持期間を過ぎると、過去の状態はさかのぼれなくなります。「先月のあの状態に戻したい」には基本的に対応できません。
  • 事故に気づくのが遅れると間に合わない — 数週間後に「実はあのとき壊れていた」と気づいても、保持期間を過ぎていれば手遅れです。
  • スナップショットも保存し続ければ費用がかかる — 節目で取るのは有効ですが、無限に貯め込めるわけではありません。世代管理のルールを決めておきましょう。

長期にわたって確実に残したいデータは、タイムトラベルとは別に恒久バックアップを用意するのが原則です。定期的にスナップショットを取得して一定世代を保持する、Cloud Storage へエクスポートして保管する、といった仕組みを別途設計しておきます。タイムトラベルは「直近の事故からの即時復旧」、恒久バックアップは「長期の保全」と、役割を分けて考えるのが安全です。

まとめ

BigQuery のタイムトラベルは、誤削除や誤更新からデータを取り戻すための、標準で使える心強い仕組みです。

  • タイムトラベルは既定で過去7日間ほどさかのぼれる短期の保険である
  • FOR SYSTEM_TIME AS OF で過去の状態を参照し、別テーブルに退避してから本番へ戻すのが安全な手順である
  • スナップショットとクローンを併用すると、節目の状態を残したり検証用に複製したりできる
  • 保持期間には限界があるため、長期保全は恒久バックアップを別途用意する

事故が起きてから慌てないために、復旧の手順を一度自分の環境で試しておくことをおすすめします。実際に FOR SYSTEM_TIME AS OF で過去の状態が取れることを確認しておけば、いざというときに落ち着いて動けます。

更新処理そのものの安全性を高めたい方は、毎朝の集計を安全に自動化する設定をまとめたBigQueryのスケジュールクエリでデータマートを毎朝自動更新するや、本番に当てる前にクエリを検証するBigQueryのSQLをテストしてデータ品質を担保するもあわせてご覧ください。