Snowflake で削除・更新したデータを戻す進め方|テーブルごとにデータ保持期間を決め、戻し方を先に試す
Snowflake で誤って変えたデータを Time Travel で戻せるのは、データ保持期間の内だけです。誤操作の前にテーブルの使い方ごとにデータ保持期間を決めておき、誤った更新や行の削除はクローンと入れ替え、テーブルの削除は UNDROP、同じ名前での作り直しは名前を変えてから UNDROP で戻します。試用の Snowflake で実行した結果とエラーの原文、データ保持期間を過ぎたあとの Fail-safe とストレージ料金の見方までを書きます。
目次 クリックで開く
たとえば、こんな場面です。夕方、ある店舗の1日分の金額を0円に直すつもりで実行した更新が、日付の条件を書き忘れたため、その店舗の行をすべて0円にしていました。気づいたのは次の日の朝です。Snowflake には過去のデータに戻す機能があると聞いていても、本番で試していなければ判断がつきません。本当に戻せるのか、何日前まで戻せるのか、料金はどうなるのか。
Snowflake を使い始めたデータ担当・情シスの方に向けて、誤操作の前に決めておくことと、起きたときの戻し方を順に書きます。テーブルごとにデータ保持期間を決め、検証用のテーブルで誤った更新と削除したテーブルを一度ずつ戻してみておけば、本番で起きた日にも慌てずに戻せます。
このあとの画面は、Snowflake の導入を支援している当社が、試用の Snowflake に作った売上テーブルで試したもので、店舗と金額は架空です。
誤操作の前に、テーブルごとのデータ保持期間を決めておく
Snowflake で誤って変えたデータを戻すときは、Time Travel という機能を使います。戻せるのは、データ保持期間(Time Travel で過去のデータを読んだり戻したりできる期間)の内だけです。
データ保持期間は、何も設定していなければ1日です。設定の名前は DATA_RETENTION_TIME_IN_DAYS です。今回作った売上テーブルも、SHOW PARAMETERS の結果は、今の値(value)も既定値(default)も 1 でした。この1日は、日付の区切りではなく、変更した時刻から24時間です。公式の例では、5日目の13時に行を更新すると、更新前のその行は6日目の13時まで読めます。
データ保持期間は、テーブルごとに変えられます。Enterprise Edition 以上なら、永続テーブル(テーブルを作るときの既定の種類)は0〜90日から選べます。Standard Edition では0か1日です。同じスキーマには、ORDERS(売上テーブル)と ORDERS_RESTORED がありました。ORDERS_RESTORED は、3節で入れ替えたあとに残った、誤った更新が入った元のテーブルです。このうち売上テーブルだけを 30日に変えました。SHOW TABLES の retention_time 列は、ORDERS が 30、ORDERS_RESTORED は 1 のままでした。
データベースやスキーマに期間を設定すると、その中で期間を指定していないテーブルは、その値になります。あとから上の階層の値を変えると、指定していないテーブルの期間がまとめて変わります。変える前に、どのテーブルが影響を受けるかを確かめます。アカウント全体を0日にすることは、公式も勧めていません。
何日にするかは、テーブルの使い方で分けます。分かれ目は、そのテーブルを作り直せるかどうかです。
公式は、ファクトテーブルのように長く使うテーブルを、永続テーブルにするよう勧めています。ETL の途中で使うような、1日もたたずに要らなくなるテーブルは、一時テーブル(TRANSIENT)にできます。そうすれば、Fail-safe(データ保持期間が過ぎたあとの7日間の備え)の分のストレージ料金がかかりません。一時テーブルは、障害から守る必要のないデータか、Snowflake の外で作り直せるデータに使います。
一時テーブルと仮テーブル(TEMPORARY)は、データ保持期間を0か1日からしか選べません。データ保持期間を2日にして一時テーブルを作ろうとすると、次のエラーになり、テーブルは作られませんでした。
SQL compilation error: invalid value [2] for parameter ‘DATA_RETENTION_TIME_IN_DAYS’
永続テーブルの期間は、誤りに気づくまでにかかる日数より長くします。月末の締めで初めて数字を突き合わせるテーブルなら、1日では、気づいたときにはもう過ぎています。ただし、期間を長くするほど、変わったデータの分のストレージ料金が増えます。だから、すべてのテーブルを長くするのではなく、作り直せないテーブルから順に決めます。ここは公式の勧めではなく、当社の考えです。
誤った更新や行の削除は、まず直前のデータを SELECT で確かめる
戻し方は、何をしてしまったかで3つに分かれます。1つ目は誤った更新や行の削除(UPDATE・DELETE)、2つ目はテーブルの削除(DROP TABLE)です。3つ目は、同じ名前でのテーブルの作り直し(CREATE OR REPLACE)です。
冒頭の例は、1つ目に当たります。ここでは、売上テーブル(4店舗・1,000行)で、渋谷店の金額をすべて0円にする更新を実行してしまったとして進めます。
最初は戻さずに、誤った更新の直前のデータを SELECT で見て、戻したい中身があるかを確かめます。どの更新の直前かは、その更新のクエリ ID で指定します。更新の直後に、同じセッションで SET BAD_QID = LAST_QUERY_ID(); を実行すると、ID を変数に控えられます。あとで気づいたときは、Snowsight の Monitoring » Query History で探します。指定できるのは、過去14日以内に実行したクエリです。
控えた ID を BEFORE(STATEMENT => $BAD_QID) に入れて SELECT すると、その更新が終わる直前のデータが返ります。今のデータ、更新の直前、1分前(AT(OFFSET => -60))を並べたのが次の画面です。
今の合計は 4,127,250、更新の直前は 5,503,500 です。差の 1,376,250 は、渋谷店の250行の合計です。行の数はどれも 1,000行なので、行は消えていません。直前のデータには、その更新の実行中にほかのセッションで確定した変更も入ります。合計が思った値と違うときは、ほかの更新が重なっていないかを先に調べます。
クエリ ID の代わりに、AT(TIMESTAMP => …) で時刻を指定することもできます。その場合は時差に注意します。セッションの既定のタイムゾーンは America/Los_Angeles です。今回の東京リージョンのアカウントでも、SQL の結果の時刻は -0700 で出ました。また、型を付けない時刻は UTC として読まれます。日本時間で書くなら、'2026-10-20 09:00:00 +09:00'::TIMESTAMP_LTZ のように時差と型を付けます。または、ALTER SESSION SET TIMEZONE = 'Asia/Tokyo'; を実行してから、時刻に ::TIMESTAMP_LTZ を付けます。
戻すときは、直前のデータでクローンを作ってから入れ替える
中身を確かめたら、誤った更新の直前のデータで、別の名前のテーブルを作ります。使うのはクローン(CLONE)です。クローンはテーブルを複製するコマンドで、BEFORE を付けると過去のデータで複製できます。クローンは、作った直後はストレージを使いません。元のテーブルかクローンのどちらかを変えると、変えた分だけストレージ使用量が増えます。元のテーブルを直接戻さないのは、入れ替える前に件数と合計を確かめるためです。
クローンを作る SQL は CREATE OR REPLACE TABLE ORDERS_RESTORED CLONE ORDERS BEFORE(STATEMENT => $BAD_QID); です。確かめたら、ALTER TABLE ORDERS SWAP WITH ORDERS_RESTORED; で入れ替えます。合計は次のように変わりました。
SWAP WITH は、2つのテーブルの名前を入れ替えます。入れ替えたあと、ORDERS の作成日時は、クローンを作った 17:33:06 になっていました。ORDERS_RESTORED の作成日時は、最初に売上テーブルを作った 17:31:27 です(どちらも次の節の写真の created_on 列)。誤った更新が入った元のテーブルが、ORDERS_RESTORED の名前で残ったわけです。
このため、入れ替えたあとは次の4つに気をつけます。1つ目は、売上テーブルの作成日時が変わることです。created_on で新しいテーブルを探す監視やスクリプトがあれば、別のテーブルに見えます。2つ目は、ORDERS_RESTORED のストレージ料金です。何が変わったかを突き合わせ終えたら削除します。3つ目は、ファイルの取り込みの記録です。クローンは、元のテーブルの取り込みの記録(ロード履歴)を引き継ぎません。COPY INTO で取り込んでいるテーブルでは、前に取り込んだファイルを、入れ替えたあとにもう一度取り込めてしまいます。
4つ目は権限です。ふだん使っているロールで、売上テーブルが読めるかを確かめます。クローンは、COPY GRANTS を付けないと元のテーブルの権限を引き継ぎません。COPY GRANTS を付けて OR REPLACE で同じ名前のテーブルを置き換えるときは、注意が要ります。このとき付くのは、元のテーブルの権限ではなく、置き換えられたほうのテーブルの権限です。前に戻したときの ORDERS_RESTORED が残っていて、同じ名前でもう一度クローンを作る場合などに起きます。そこで本番では、毎回新しい名前で、COPY GRANTS を付けてクローンを作ります。入れ替えには、両方のテーブルの OWNERSHIP が要ります。
テーブルを削除してしまったら、UNDROP で戻す
2つ目は、テーブルそのものを削除した場合です。売上テーブルを DROP TABLE で削除してから件数を数えると、次のエラーになりました。
SQL compilation error: Object ‘ORDERS’ does not exist or not authorized. Your primary role ACCOUNTADMIN or one of your secondary roles must have at least one privilege granted on TABLE ORDERS.
このエラーは、テーブルが無いのか、権限が無いのかを区別しません。削除したかどうかは、SHOW TABLES に HISTORY を付けて確かめます。削除したテーブルも、データ保持期間が過ぎるまでは一覧に出て、dropped_on 列に削除した日時が入ります。
一覧にあれば、UNDROP TABLE ORDERS; で戻せます。売上テーブルは 1,000行・5,503,500 で戻り、0円の行もありませんでした。UNDROP は新しいテーブルを作るのではなく、削除する直前のテーブルをそのまま戻します。
UNDROP には、そのテーブルの OWNERSHIP と、戻す先のスキーマの CREATE TABLE 権限が要ります。SELECT しかできないロールでは戻せません。誰のロールで UNDROP と入れ替えを実行するかは、先に決めておきます。ロールの組み方は、部署ごとのロールと行の見え方を確かめた記事に書きました。戻す先のスキーマについて、公式の UNDROP TABLE のページには、削除したときのスキーマに戻るとあります。一方、Time Travel のページには、今のスキーマでだけ戻せるとあります。どちらでも困らないよう、今回は USE SCHEMA で元のスキーマに移ってから実行しました。
同じ名前で作り直したテーブルは、名前を変えてから UNDROP する
3つ目は、同じ名前でテーブルを作り直してしまった場合です。新宿店だけを抜き出すつもりで、新しいテーブルの名前に元と同じ ORDERS を書いてしまった、という例です。実行したのは CREATE OR REPLACE TABLE ORDERS AS SELECT * FROM ORDERS WHERE STORE = '新宿店'; で、売上テーブルは新宿店の250行・1,376,000 になりました。
CREATE OR REPLACE は、前のテーブルを削除してから作り直します。前のテーブルは、削除したテーブルとして残っています。ただ、新しい ORDERS があるまま UNDROP TABLE ORDERS を実行すると、次のエラーになりました。
SQL compilation error: Object ‘ORDERS’ already exists.
同じ名前のテーブルがあると、UNDROP は実行できません。そこで、今のテーブルを ALTER TABLE ORDERS RENAME TO ORDERS_OVERWRITTEN; で別の名前にしてから、UNDROP を実行します。作り直す前の売上テーブルが、1,000行・5,503,500 で戻りました。
同じ名前で何度も作り直したテーブルでは、UNDROP は最後に削除したものから戻します。もっと前のものが要るときは、戻ったテーブルの名前をまた変えて、もう一度 UNDROP します。作り直すたびに、前のテーブルの分のストレージ料金もかかります。作業用のテーブルを毎回作り直しているなら、一時テーブルにできないかを、1節の「テーブルの使い方と、データ保持期間」の図で確かめます。
データ保持期間を過ぎたあとは、自分では戻せない
データ保持期間を過ぎた永続テーブルのデータは、さらに7日間、Fail-safe に残ります。ただし Fail-safe は、利用者が過去のデータを読むためのものではありません。大きな障害でデータが失われたときに、Snowflake が回復を試みるためのもので、戻る保証もありません。回復は Snowflake のサポートに依頼するもので、数時間から数日かかることがあります。回復の処理にも料金がかかります。一時テーブルと仮テーブルには、Fail-safe がありません。守りたい期間は、データ保持期間で取っておきます。
データ保持期間のあいだも、そのあとの Fail-safe の7日間も、過去のデータにはストレージ料金がかかります。残るのは、更新や行の削除で変わった分だけです。テーブル全体が残るのは、テーブルを DROP したときと TRUNCATE したときです。期間を長くすると、その分ストレージ使用量が増えます。請求のされ方は、Snowflake の料金とコストの記事に書きました。
テーブルごとのストレージ使用量は、TABLE_STORAGE_METRICS で見られます。TIME_TRAVEL_BYTES がデータ保持期間の分、FAILSAFE_BYTES が Fail-safe の分です。Information Schema のこのビューは、ACCOUNTADMIN 以外のロールで実行すると0行になります。0行でも、ストレージを使っていないとは限りません。数字は1〜2時間遅れて反映されることがあり、SNOWFLAKE.ACCOUNT_USAGE にある同じ名前のビューなら、遅れは最大90分です。画面では、ACCOUNTADMIN で Admin » Cost management » Consumption を開きます。Usage Type を Storage にすると、Fail-safe などの内訳が出ます。
今回は、UNDROP とデータ保持期間の変更を試した直後に、このビューを見ました。今回作った ORDERS についての行は、ACTIVE_BYTES・TIME_TRAVEL_BYTES・FAILSAFE_BYTES の3列とも 0 でした。このため、ストレージ使用量は数字では確かめられませんでした。また、スキーマ名(SALES)だけで絞ったため、別の検証で削除したデータベースのものと見られるテーブルも出ました。それらの行には、FAILSAFE_BYTES に値がありました。見るときは、データベース名(TABLE_CATALOG)でも絞ります。
冒頭の例の更新が戻せるかは、更新から何時間後に気づいたかで決まる
冒頭の例では、夕方の更新に次の日の朝気づきました。24時間以内なので、データ保持期間が既定の1日のままでも、直前のデータのクローンと入れ替えて戻せます。金曜の夕方の更新に週明けに気づいたのなら、既定の1日では戻せません。
まず、誤った更新が起きたら困るテーブルを1つ選び、SHOW TABLES でデータ保持期間を見てください。ふだん誤りに何日で気づいているかと比べ、足りなければ延ばします(Enterprise Edition 以上なら90日まで)。そのうえで、検証用のデータベースで下の SQL を実行し、誤った更新とテーブルの削除を一度ずつ戻してみます。クエリ ID の控え方と、UNDROP を実行するロールが、本番で起きる前に決まります。
当社の Snowflake 導入支援では、アーキテクチャ設計の工程に、Time Travel のデータ保持期間の設計を入れています。
試したときの SQL と環境
画面は、試用の Snowflake(Enterprise Edition・AWS の東京リージョン)で、2026年10月に作りました。ロールは ACCOUNTADMIN、ウェアハウスは X-Small です。売上テーブルは GENERATOR で4店舗・1,000行を作り、金額は式で決めています。
次の SQL は、テーブルを作る部分と3つの戻し方の部分を、実行した順に並べたものです。データベースとスキーマの名前は mydb・sales に置き換え、直前のデータを見る SELECT は件数と合計だけに縮めました。CREATE OR REPLACE と DROP を含むので、試すときは本番とは別のデータベースで実行します。
CREATE DATABASE IF NOT EXISTS mydb;
CREATE SCHEMA IF NOT EXISTS mydb.sales;
USE SCHEMA mydb.sales;
CREATE OR REPLACE TABLE ORDERS AS
SELECT SEQ4() + 1 AS ORDER_ID,
DATEADD(DAY, MOD(SEQ4(), 30), '2026-09-01'::DATE) AS ORDER_DATE,
DECODE(MOD(SEQ4(), 4), 0, '渋谷店', 1, '新宿店', 2, '池袋店', '横浜店') AS STORE,
1000 + MOD((SEQ4() + 1) * 7919, 9000) AS AMOUNT
FROM TABLE(GENERATOR(ROWCOUNT => 1000));
-- 誤った更新 → 直前のデータを確かめる → クローンと入れ替える
UPDATE ORDERS SET AMOUNT = 0 WHERE STORE = '渋谷店';
SET BAD_QID = LAST_QUERY_ID();
SELECT COUNT(*), SUM(AMOUNT) FROM ORDERS BEFORE(STATEMENT => $BAD_QID);
CREATE OR REPLACE TABLE ORDERS_RESTORED CLONE ORDERS BEFORE(STATEMENT => $BAD_QID);
ALTER TABLE ORDERS SWAP WITH ORDERS_RESTORED;
-- テーブルの削除 → 履歴を見る → 戻す
DROP TABLE ORDERS;
SHOW TABLES HISTORY LIKE 'ORDERS%' IN SCHEMA mydb.sales;
UNDROP TABLE ORDERS;
-- データ保持期間を30日にする
ALTER TABLE ORDERS SET DATA_RETENTION_TIME_IN_DAYS = 30;
-- 同じ名前での作り直し → 名前を変えて戻す
CREATE OR REPLACE TABLE ORDERS AS SELECT * FROM ORDERS WHERE STORE = '新宿店';
ALTER TABLE ORDERS RENAME TO ORDERS_OVERWRITTEN;
UNDROP TABLE ORDERS;
本番の手順書に写すときは、クローンの文から OR REPLACE を外し、COPY GRANTS を足します。今回は試し終えたあと、データベースと売上テーブルのデータ保持期間を0にしてから、データベースを削除しました。データ保持期間の分のストレージ料金を残さないためです(永続テーブルの Fail-safe の7日分は残ります)。
ここに書いた結果は、1回の検証で見たものです。ストレージ使用量と料金は、数字で確かめられていません。試したのは誤った更新(UPDATE)で、行の削除(DELETE)からの戻しは試していません。ほかに試していないのは、COPY GRANTS を付けたクローン、Fail-safe からの回復をサポートに頼むこと、データ保持期間を過ぎたあとの読み出し、90日の設定、仮テーブル、時刻での指定、ACCOUNTADMIN 以外のロールでの UNDROP と入れ替え、スキーマやデータベース単位の UNDROP、Snowsight の画面だけで戻す方法です。
データ分析・データ基盤支援
Snowflake・BigQuery・Treasure Dataといったデータ基盤の選定から、基幹・SaaS・広告・POSに散らばったデータの集約、dbtでの指標統一、Lookerなどでのダッシュボード化までを支援します。
