Snowflakeのストレージライフサイクルポリシーを試してみた

Snowflakeのストレージライフサイクルポリシーを試してみた

2026.03.11

さがらです。

Snowflakeの新機能として、ストレージライフサイクルポリシーがリリースされています。テーブル内の行データを、経過日数などの条件に基づいて低コストのアーカイブストレージに自動移行したり、期限切れとして永続的に削除したりできる機能です。

https://docs.snowflake.com/en/release-notes/2025/other/2025-11-07-storage-lifecycle-policies-ga

https://docs.snowflake.com/en/user-guide/storage-management/storage-lifecycle-policies

これまではデータの保持期間管理やストレージコスト最適化のために手動でデータ移行・削除を行う必要がありましたが、この機能によりポリシーベースで自動化できるようになります。実際に試してみたので、手順と確認結果をまとめます。

前提条件

  • 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です。

2026-03-10_09h05_22

データの分布も確認しておきます。

-- 日数ごとのデータ分布を確認
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;

2026-03-10_09h05_48

権限の確認

ストレージライフサイクルポリシーの作成には、スキーマに対する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です。

2026-03-10_09h06_31

2. テーブルにポリシーを適用

作成したポリシーをORDERSテーブルに適用します。ON句には、ポリシーの引数に対応するテーブルの列を指定します。

ALTER TABLE ORDERS ADD STORAGE LIFECYCLE POLICY ORDERS_ARCHIVE_POLICY
    ON (ORDER_DATE);

Statement executed successfully.と表示されればOKです。

2026-03-10_09h07_06

ポリシーがテーブルに適用されていることを確認します。

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です。

2026-03-10_09h07_59

3. 期限切れポリシーの作成と適用

続いて、期限切れ(expire)ポリシーも試してみます。180日以上前のデータを永続的に削除するポリシーを作成します。

期限切れポリシーは、ARCHIVE_TIERARCHIVE_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です。

2026-03-10_09h13_28

適用前のテーブル件数も確認しておきます。

SELECT COUNT(*) AS TOTAL_RECORDS FROM ORDERS_EXPIRE_TEST;

ORDERSテーブルから30件すべてコピーされていることが確認できます。

2026-03-10_09h13_55

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です。

2026-03-11_11h26_21

また、テーブルの件数を確認してみます。

-- アーカイブ後のテーブル件数確認
SELECT COUNT(*) AS REMAINING_RECORDS FROM ORDERS;

90日以上前のデータ(ORDER_ID 1-20の20件)がアーカイブされ、90日以内のデータ(ORDER_ID 21-30の10件)のみが残っていました。そのため想定通りの挙動となります。

2026-03-11_11h26_48

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日以上前のデータが取得できましたので、アーカイブされたデータからの復元が確認できました。

2026-03-11_11h28_07

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です。

2026-03-11_11h29_23

テーブルの件数を確認してみます。

-- 期限切れ後のテーブル件数確認
SELECT COUNT(*) AS REMAINING_RECORDS FROM ORDERS_EXPIRE_TEST;

180日以上前のデータ(ORDER_ID 1-10の10件)が削除されており、残りの20件が返ってきましたので問題なしです。

2026-03-11_11h29_52

念のため、削除されたデータが本当に存在しないことも確認しておきます。

-- 180日以上前のデータが存在しないことを確認
SELECT COUNT(*) AS OLD_RECORDS
FROM ORDERS_EXPIRE_TEST
WHERE DATEDIFF(DAY, ORDER_DATE, CURRENT_TIMESTAMP()) >= 180;

0件と返ってきました。このため、期限切れポリシーにより、条件に該当するデータが永続的に削除されたことが確認できました。

2026-03-11_11h30_26

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)されている機能ですので、データ保持ポリシーの自動化を検討されている方はぜひ試してみてください。

この記事をシェアする

FacebookHatena blogX

関連記事