Snowflakeのストレージライフサイクルポリシーを試してみた
さがらです。
Snowflakeの新機能として、ストレージライフサイクルポリシーがリリースされています。テーブル内の行データを、経過日数などの条件に基づいて低コストのアーカイブストレージに自動移行したり、期限切れとして永続的に削除したりできる機能です。
これまではデータの保持期間管理やストレージコスト最適化のために手動でデータ移行・削除を行う必要がありましたが、この機能によりポリシーベースで自動化できるようになります。実際に試してみたので、手順と確認結果をまとめます。
前提条件
- Snowflake: AWS東京リージョン、Enterpriseエディション
- 本機能のステータス: 2025年11月7日に一般提供(GA)済み
- 必要な権限:
- ポリシーの作成: スキーマに対する
CREATE STORAGE LIFECYCLE POLICY権限(親データベース・親スキーマへのUSAGE権限も必要) - ポリシーの適用・解除: ポリシーに対する
APPLYまたはOWNERSHIP権限、かつテーブルに対するOWNERSHIP権限(またはアカウントレベルのAPPLY STORAGE LIFECYCLE POLICY権限) - アーカイブデータの取得(
CREATE TABLE ... FROM ARCHIVE OF): ソーステーブルに対するOWNERSHIP権限
- ポリシーの作成: スキーマに対する
- 対応テーブル: 標準Snowflakeテーブル、動的テーブル、およびオートリフレッシュなしのインタラクティブテーブル
- アーカイブの対応クラウド:
- COOL層: AWS、Microsoft Azure
- COLD層: AWSのみ
- 期限切れの対応クラウド: AWS、Microsoft Azure、Google Cloud(全プロバイダー対応)
ストレージライフサイクルポリシーの概要
ストレージライフサイクルポリシーは、テーブルの行データに対してライフサイクル管理を自動化するスキーマレベルのオブジェクトです。主に以下の2つの機能があります。
アーカイブ
条件を満たした行データを低コストのアーカイブストレージに移行します。アーカイブ先には2つの層があります。
| 項目 | COOL層 | COLD層 |
|---|---|---|
| コスト | 通常ストレージより低い | COOL層の約4分の1 |
| 最小保持期間 | 90日 | 180日 |
| データ取得速度 | 即座に取得可能 | 最大48時間 |
| 対応クラウド | AWS、Azure | AWSのみ |
重要: 一度テーブルに設定したアーカイブ層(COOL/COLD)は、テーブルの生涯にわたり変更できません。
期限切れ(Expire)
条件を満たした行データを永続的に削除します。アーカイブと組み合わせて「アーカイブ期間経過後に削除」という運用も可能ですし、アーカイブなしで直接削除することもできます。
実行スケジュール
- ポリシーをテーブルに適用してから、初回実行まで約24時間の待機がある
- その後は毎日自動実行(共有コンピュートリソースを使用)
- 大きな処理は1回の日次実行で完了せず、複数回の日次実行にまたがることがある
事前準備
検証用テーブルの作成
まず、検証用のデータベース・スキーマ・テーブルを作成し、サンプルデータを投入します。
USE ROLE SYSADMIN;
-- 検証用のデータベースとスキーマを作成
CREATE DATABASE IF NOT EXISTS SLP_TEST_DB;
CREATE SCHEMA IF NOT EXISTS SLP_TEST_DB.SLP_TEST_SCHEMA;
USE DATABASE SLP_TEST_DB;
USE SCHEMA SLP_TEST_SCHEMA;
-- 検証用テーブル(ECサイトの注文データ)
-- 古いデータ(90日以上前)をアーカイブ対象とするため、日付を分散させている
CREATE OR REPLACE TABLE ORDERS (
ORDER_ID NUMBER,
CUSTOMER_NAME VARCHAR,
ORDER_DATE TIMESTAMP,
PRODUCT_NAME VARCHAR,
QUANTITY NUMBER,
UNIT_PRICE NUMBER(10,2),
TOTAL_AMOUNT NUMBER(10,2),
STATUS VARCHAR,
REGION VARCHAR
);
-- サンプルデータの投入(30レコード)
-- 古い注文(180日以上前): ORDER_ID 1-10 → アーカイブ&期限切れ対象
-- やや古い注文(90-180日前): ORDER_ID 11-20 → アーカイブ対象
-- 新しい注文(90日以内): ORDER_ID 21-30 → 対象外
INSERT INTO ORDERS VALUES
(1, 'Taro Yamada', DATEADD(DAY, -200, CURRENT_TIMESTAMP()), 'Wireless Mouse', 2, 25.00, 50.00, 'Delivered', 'Japan'),
(2, 'Hanako Sato', DATEADD(DAY, -195, CURRENT_TIMESTAMP()), 'USB Keyboard', 1, 45.00, 45.00, 'Delivered', 'Japan'),
(3, 'John Smith', DATEADD(DAY, -210, CURRENT_TIMESTAMP()), 'Monitor Stand', 1, 80.00, 80.00, 'Delivered', 'US'),
(4, 'Emily Chen', DATEADD(DAY, -185, CURRENT_TIMESTAMP()), 'Webcam HD', 1, 60.00, 60.00, 'Delivered', 'US'),
(5, 'Kenji Tanaka', DATEADD(DAY, -220, CURRENT_TIMESTAMP()), 'USB Hub', 3, 15.00, 45.00, 'Delivered', 'Japan'),
(6, 'Maria Garcia', DATEADD(DAY, -205, CURRENT_TIMESTAMP()), 'Laptop Sleeve', 1, 30.00, 30.00, 'Delivered', 'US'),
(7, 'Yuki Suzuki', DATEADD(DAY, -190, CURRENT_TIMESTAMP()), 'Mouse Pad', 2, 12.00, 24.00, 'Delivered', 'Japan'),
(8, 'David Lee', DATEADD(DAY, -215, CURRENT_TIMESTAMP()), 'HDMI Cable', 2, 10.00, 20.00, 'Delivered', 'US'),
(9, 'Akiko Watanabe', DATEADD(DAY, -188, CURRENT_TIMESTAMP()), 'Desk Lamp', 1, 35.00, 35.00, 'Delivered', 'Japan'),
(10, 'Robert Brown', DATEADD(DAY, -230, CURRENT_TIMESTAMP()), 'Phone Stand', 1, 20.00, 20.00, 'Delivered', 'US'),
(11, 'Takeshi Ito', DATEADD(DAY, -120, CURRENT_TIMESTAMP()), 'Wireless Charger', 1, 40.00, 40.00, 'Delivered', 'Japan'),
(12, 'Sarah Wilson', DATEADD(DAY, -110, CURRENT_TIMESTAMP()), 'Bluetooth Speaker', 1, 55.00, 55.00, 'Delivered', 'US'),
(13, 'Naomi Kobayashi', DATEADD(DAY, -130, CURRENT_TIMESTAMP()), 'Portable SSD', 1, 90.00, 90.00, 'Delivered', 'Japan'),
(14, 'Michael Johnson', DATEADD(DAY, -100, CURRENT_TIMESTAMP()), 'Ethernet Cable', 5, 8.00, 40.00, 'Shipped', 'US'),
(15, 'Yuko Nakamura', DATEADD(DAY, -140, CURRENT_TIMESTAMP()), 'Power Strip', 2, 25.00, 50.00, 'Delivered', 'Japan'),
(16, 'Chris Taylor', DATEADD(DAY, -95, CURRENT_TIMESTAMP()), 'Screen Protector', 3, 12.00, 36.00, 'Delivered', 'US'),
(17, 'Rina Hashimoto', DATEADD(DAY, -150, CURRENT_TIMESTAMP()), 'Keyboard Cover', 1, 18.00, 18.00, 'Delivered', 'Japan'),
(18, 'Jennifer Davis', DATEADD(DAY, -105, CURRENT_TIMESTAMP()), 'USB Flash Drive', 2, 15.00, 30.00, 'Delivered', 'US'),
(19, 'Shota Yamamoto', DATEADD(DAY, -160, CURRENT_TIMESTAMP()), 'Cable Organizer', 1, 22.00, 22.00, 'Delivered', 'Japan'),
(20, 'Anna Martinez', DATEADD(DAY, -115, CURRENT_TIMESTAMP()), 'Laptop Stand', 1, 70.00, 70.00, 'Delivered', 'US'),
(21, 'Ryota Fujita', DATEADD(DAY, -30, CURRENT_TIMESTAMP()), 'Mechanical Keyboard', 1, 120.00, 120.00, 'Shipped', 'Japan'),
(22, 'Lisa Anderson', DATEADD(DAY, -25, CURRENT_TIMESTAMP()), 'Gaming Mouse', 1, 65.00, 65.00, 'Shipped', 'US'),
(23, 'Mika Ogawa', DATEADD(DAY, -20, CURRENT_TIMESTAMP()), 'Noise Cancelling Headphones', 1, 150.00, 150.00, 'Processing', 'Japan'),
(24, 'James White', DATEADD(DAY, -15, CURRENT_TIMESTAMP()), 'Webcam 4K', 1, 100.00, 100.00, 'Processing', 'US'),
(25, 'Haruka Morita', DATEADD(DAY, -10, CURRENT_TIMESTAMP()), 'Drawing Tablet', 1, 200.00, 200.00, 'Processing', 'Japan'),
(26, 'Tom Harris', DATEADD(DAY, -7, CURRENT_TIMESTAMP()), 'USB-C Dock', 1, 85.00, 85.00, 'Processing', 'US'),
(27, 'Sakura Kimura', DATEADD(DAY, -5, CURRENT_TIMESTAMP()), 'Microphone', 1, 75.00, 75.00, 'Processing', 'Japan'),
(28, 'Kevin Clark', DATEADD(DAY, -3, CURRENT_TIMESTAMP()), 'Monitor Light', 1, 50.00, 50.00, 'Processing', 'US'),
(29, 'Aoi Shimizu', DATEADD(DAY, -2, CURRENT_TIMESTAMP()), 'Ergonomic Chair Mat', 1, 45.00, 45.00, 'Processing', 'Japan'),
(30, 'Rachel Green', DATEADD(DAY, -1, CURRENT_TIMESTAMP()), 'Cable Management Kit', 1, 28.00, 28.00, 'Processing', 'US');
テーブルにデータが投入されたことを確認します。
SELECT COUNT(*) AS TOTAL_RECORDS FROM ORDERS;
30件が返ればOKです。

データの分布も確認しておきます。
-- 日数ごとのデータ分布を確認
SELECT
CASE
WHEN DATEDIFF(DAY, ORDER_DATE, CURRENT_TIMESTAMP()) >= 180 THEN '180日以上前'
WHEN DATEDIFF(DAY, ORDER_DATE, CURRENT_TIMESTAMP()) >= 90 THEN '90-179日前'
ELSE '90日以内'
END AS AGE_CATEGORY,
COUNT(*) AS RECORD_COUNT
FROM ORDERS
GROUP BY AGE_CATEGORY
ORDER BY AGE_CATEGORY;

権限の確認
ストレージライフサイクルポリシーの作成には、スキーマに対するCREATE STORAGE LIFECYCLE POLICY権限が必要です。
USE ROLE ACCOUNTADMIN;
-- SYSADMINにポリシー作成権限を付与する場合
GRANT CREATE STORAGE LIFECYCLE POLICY ON SCHEMA SLP_TEST_DB.SLP_TEST_SCHEMA TO ROLE SYSADMIN;
ストレージライフサイクルポリシーを試してみる
1. アーカイブポリシーの作成
まず、90日以上前の注文データをCOOL層にアーカイブするポリシーを作成します。COOL層の最小保持期間は90日のため、ARCHIVE_FOR_DAYSは90を指定します。
USE ROLE SYSADMIN;
USE DATABASE SLP_TEST_DB;
USE SCHEMA SLP_TEST_SCHEMA;
CREATE STORAGE LIFECYCLE POLICY ORDERS_ARCHIVE_POLICY
AS (order_date TIMESTAMP)
RETURNS BOOLEAN ->
order_date < TO_DATE(DATEADD(DAY, -90, CURRENT_TIMESTAMP()))
ARCHIVE_TIER = COOL
ARCHIVE_FOR_DAYS = 90
COMMENT = '90日以上前の注文データをCOOL層にアーカイブする';
ポリシーが作成されたことを確認します。
SELECT GET_DDL('POLICY', 'ORDERS_ARCHIVE_POLICY');
作成したポリシーのDDLが返ればOKです。

2. テーブルにポリシーを適用
作成したポリシーをORDERSテーブルに適用します。ON句には、ポリシーの引数に対応するテーブルの列を指定します。
ALTER TABLE ORDERS ADD STORAGE LIFECYCLE POLICY ORDERS_ARCHIVE_POLICY
ON (ORDER_DATE);
Statement executed successfully.と表示されればOKです。

ポリシーがテーブルに適用されていることを確認します。
SELECT * FROM TABLE(
INFORMATION_SCHEMA.POLICY_REFERENCES(
REF_ENTITY_NAME => 'SLP_TEST_DB.SLP_TEST_SCHEMA.ORDERS',
REF_ENTITY_DOMAIN => 'TABLE'
)
) WHERE POLICY_KIND = 'STORAGE_LIFECYCLE_POLICY';
ポリシー情報が返ればOKです。

3. 期限切れポリシーの作成と適用
続いて、期限切れ(expire)ポリシーも試してみます。180日以上前のデータを永続的に削除するポリシーを作成します。
期限切れポリシーは、ARCHIVE_TIERとARCHIVE_FOR_DAYSを指定しないで作成します。
この時点ではまだアーカイブは実行されていないため、ORDERSテーブルには30件すべてのデータが残っています。期限切れポリシーの検証用に、このデータをコピーして別テーブルを作成します。
-- 期限切れ専用のテーブルを作成してデータを投入(この時点でORDERSには30件のデータがある)
CREATE OR REPLACE TABLE ORDERS_EXPIRE_TEST AS
SELECT * FROM ORDERS;
-- 180日以上前のデータを期限切れにするポリシー
CREATE STORAGE LIFECYCLE POLICY ORDERS_EXPIRE_POLICY
AS (order_date TIMESTAMP)
RETURNS BOOLEAN ->
order_date < TO_DATE(DATEADD(DAY, -180, CURRENT_TIMESTAMP()))
COMMENT = '180日以上前の注文データを期限切れ(永続削除)にする';
-- テーブルにポリシーを適用
ALTER TABLE ORDERS_EXPIRE_TEST ADD STORAGE LIFECYCLE POLICY ORDERS_EXPIRE_POLICY
ON (ORDER_DATE);
Statement executed successfully.と表示されればOKです。

適用前のテーブル件数も確認しておきます。
SELECT COUNT(*) AS TOTAL_RECORDS FROM ORDERS_EXPIRE_TEST;
ORDERSテーブルから30件すべてコピーされていることが確認できます。

4. アーカイブ実行結果の確認(以降の章は、ポリシー適用後24時間経過してから確認)
両ポリシーの適用から約24時間後、まずアーカイブポリシーの実行履歴を確認します。
SELECT * FROM TABLE(
INFORMATION_SCHEMA.STORAGE_LIFECYCLE_POLICY_HISTORY(
REF_ENTITY_NAME => 'SLP_TEST_DB.SLP_TEST_SCHEMA.ORDERS',
REF_ENTITY_DOMAIN => 'TABLE',
TIME_RANGE_START => DATEADD('DAY', -1, CURRENT_TIMESTAMP()),
RESULT_LIMIT => 100
)
);
実行履歴が表示され、アーカイブされた行数が確認できればOKです。

また、テーブルの件数を確認してみます。
-- アーカイブ後のテーブル件数確認
SELECT COUNT(*) AS REMAINING_RECORDS FROM ORDERS;
90日以上前のデータ(ORDER_ID 1-20の20件)がアーカイブされ、90日以内のデータ(ORDER_ID 21-30の10件)のみが残っていました。そのため想定通りの挙動となります。

5. アーカイブデータの取得
アーカイブされたデータを取得するには、CREATE TABLE ... FROM ARCHIVE OFコマンドを使用します。このコマンドにより、アーカイブデータのコピーを含む新しいテーブルが作成されます。
-- アーカイブされたデータを取得して新しいテーブルに格納
CREATE TABLE ORDERS_ARCHIVED
FROM ARCHIVE OF ORDERS AS o
WHERE o.ORDER_DATE < TO_DATE(DATEADD(DAY, -180, CURRENT_TIMESTAMP()));
取得したデータを確認します。
SELECT * FROM ORDERS_ARCHIVED ORDER BY ORDER_ID;
180日以上前のデータが取得できましたので、アーカイブされたデータからの復元が確認できました。

6. 期限切れ実行結果の確認
続いて、期限切れポリシーの実行履歴を確認します。
SELECT * FROM TABLE(
INFORMATION_SCHEMA.STORAGE_LIFECYCLE_POLICY_HISTORY(
REF_ENTITY_NAME => 'SLP_TEST_DB.SLP_TEST_SCHEMA.ORDERS_EXPIRE_TEST',
REF_ENTITY_DOMAIN => 'TABLE',
TIME_RANGE_START => DATEADD('DAY', -1, CURRENT_TIMESTAMP()),
RESULT_LIMIT => 100
)
);
実行履歴が表示され、期限切れとして削除された行数が確認できればOKです。

テーブルの件数を確認してみます。
-- 期限切れ後のテーブル件数確認
SELECT COUNT(*) AS REMAINING_RECORDS FROM ORDERS_EXPIRE_TEST;
180日以上前のデータ(ORDER_ID 1-10の10件)が削除されており、残りの20件が返ってきましたので問題なしです。

念のため、削除されたデータが本当に存在しないことも確認しておきます。
-- 180日以上前のデータが存在しないことを確認
SELECT COUNT(*) AS OLD_RECORDS
FROM ORDERS_EXPIRE_TEST
WHERE DATEDIFF(DAY, ORDER_DATE, CURRENT_TIMESTAMP()) >= 180;
0件と返ってきました。このため、期限切れポリシーにより、条件に該当するデータが永続的に削除されたことが確認できました。

7. テーブルからポリシーを解除
不要になったポリシーはテーブルから関連付けを解除できます。これはポリシーオブジェクト自体の削除ではなく、テーブルとの関連付けを外す操作です。
ALTER TABLE ORDERS DROP STORAGE LIFECYCLE POLICY;
注意点・制限事項
- 1テーブルに1ポリシーのみ: 1つのテーブルに設定できるストレージライフサイクルポリシーは1つだけです。ポリシーを変更したい場合は、
ALTER TABLE ... DROP STORAGE LIFECYCLE POLICYで既存のポリシーの関連付けを解除してから、ALTER TABLE ... ADD STORAGE LIFECYCLE POLICYで新しいポリシーを適用する必要があります - アーカイブ層は変更不可: 一度テーブルに設定したアーカイブ層(COOL/COLD)はテーブルの生涯にわたり変更できません。変更が必要な場合はSnowflakeサポートへの依頼が必要です
- アーカイブデータは直接クエリ不可: アーカイブされたデータは通常のSELECT文では参照できません。
CREATE TABLE ... FROM ARCHIVE OFで新しいテーブルにコピーする必要があります - アーカイブ済みデータはレプリケートされない: ポリシー定義とテーブルへの関連付けはレプリケートされますが、ポリシー自体はターゲットアカウントでは実行されません。また、アーカイブ済みデータもレプリケートされないため、フェイルオーバー後にターゲットアカウントでアーカイブデータを利用することはできません
- 対応しないオブジェクト: WORMスナップショット、共有テーブル(プロバイダー・コンシューマー)、ネイティブアプリ、外部アクセス機能付きUDFなどには適用できません
- 課金に注意: ポリシーは毎日自動実行されるため、不要になったら早めに削除しましょう。また、データはアーカイブ後もFail-safe(7日間)+Time Travel保持期間中は通常ストレージとアーカイブストレージの両方で課金されます
- COLD層の最小保持期間: COLD層は180日の最小保持期間があり、この期間内にテーブルを削除すると追加料金が発生します(COOL層は90日)
最後に
Snowflakeのストレージライフサイクルポリシーを試してみました。
ポリシーをSQLで定義してテーブルに適用するだけで、データのアーカイブや期限切れを自動化できるのは非常に便利だと感じました。特に、ログデータやイベントデータなど、時間の経過とともにアクセス頻度が下がるデータを大量に扱うユースケースでは、ストレージコストの削減に大きく貢献しそうです。
一方で、アーカイブ層が一度設定すると変更できない点や、アーカイブされたデータを参照するにはCREATE TABLE ... FROM ARCHIVE OFで別テーブルにコピーする必要がある点は、運用設計の段階でよく考えておく必要がありそうです。
既に一般提供(GA)されている機能ですので、データ保持ポリシーの自動化を検討されている方はぜひ試してみてください。






