SELECTSELECT

SELECT

Snowflakeの未使用テーブルを見つけ出す

By Ian WhitestoneMar 19, 20235 min read

このページはEnglishDeutschEspañolFrançaisItalianoPortuguêsでもご覧いただけます。

SnowflakeのストレージコストはSnowflake全体の支出に占める割合こそ小さいものの、多くのお客様のアカウントには使われていないテーブルが大量に残り、無駄なコストを生み続けています。活用されておらず、ビジネス価値も生まず、法令上の保管義務もないデータセットは、削除するのが得策です。

未使用データセットの削除は、Snowflakeコストを削減したいチームにとって即効性のある一手です。あわせて、セキュリティの強化や、情報漏洩・データ流出のリスク低減にもつながります。保有データが少ないほど、意図しないアクセスにさらされる範囲も狭まります。

さらに、未使用テーブルの削除はデータウェアハウス全体の使いやすさの向上にも寄与します。未使用データセットには古くなったデータや本来参照されるべきでないデータが含まれていることが多く、削除しておくことで混乱やレポートの誤りを未然に防げます。

本記事では、access_historyアカウント使用状況ビューを用いて、Snowflakeの未使用テーブルを特定する方法を解説します。

SQLの最終形だけを確認したい方は、こちらへどうぞ。

Snowflake Access Historyビュー

Access Historyは、Snowflake DatabaseAccount Usageスキーマに用意されているビューで、Enterprise Edition以上のすべてのSnowflakeアカウントで利用できます。Access Historyを使うと、各クエリが直接または間接的にアクセスしたSnowflakeオブジェクト(テーブル、ビュー、カラム)を追跡できます。

Direct ObjectsとBase Objectsの違い

クエリがアクセスしたカラムを把握するには、direct_objects_accessedbase_objects_accessedという2つのカラムに注目します。両者の最大の違いは、ビューの扱い方です。次のビュー定義を例に考えてみましょう。

create or replace view orders_view as (
	select *
	from orders
	where
		not test
		and success
);

select * from orders_viewというクエリは、orders_viewオブジェクトに直接アクセスし、そのベースとなるordersテーブルには間接的にアクセスしています。そのためaccess_historyでは、direct_objects_accessedカラムにorders_viewが、base_objects_accessedにはordersが記録されます。

テーブルが未使用かを判断する際は、ビュー経由で間接的にアクセスされたクエリも漏らさず拾うために、base_objects_accessedを使うことが重要です。

base_objects_accessedのパース

base_objects_accessedは、クエリ実行中にアクセスされたすべてのベースデータオブジェクトを格納するJSON配列です。公式ドキュメントに掲載されている、このカラムの内容例を以下に示します。

1[\
\
2  {\
\
3    "columns": [\
\
4      {\
\
5        "columnId": 68610,\
\
6        "columnName": "CONTENT"\
\
7      }\
\
8    ],\
\
9    "objectDomain": "Table",\
\
10    "objectId": 66564,\
\
11    "objectName": "GOVERNANCE.TABLES.T1"\
\
12  }\
\
13]

各クエリでアクセスされたオブジェクトの配列は、lateral flattenでオブジェクトごとに1行へ展開し、テーブルオブジェクトのみに絞り込めます。具体的には次のとおりです。

with
access_history as (
    select *
    from snowflake.account_usage.access_history
),
access_history_flattened as (
    select
        access_history.query_id,
        access_history.query_start_time,
        access_history.user_name,
        objects_accessed.value:objectId::integer as table_id,
        objects_accessed.value:objectName::text as object_name,
        objects_accessed.value:objectDomain::text as object_domain,
        objects_accessed.value:columns as columns_array

コードを展開

テーブルが最後にクエリ/アクセスされた日時を特定する

先ほどのクエリで得た「フラット化済み」のaccess_historyを使えば、テーブルが最後にアクセスされた正確な日時と、そのクエリを実行したユーザーを特定できます。


with
access_history as (
    select *
    from snowflake.account_usage.access_history
),
access_history_flattened as (
    select
        access_history.query_id,
        access_history.query_start_time,
        access_history.user_name,
        objects_accessed.value:objectId::integer as table_id,
        objects_accessed.value:objectName::text as object_name,
        objects_accessed.value:objectDomain::text as object_domain,
        objects_accessed.value:columns as columns_array

コードを展開

テーブルのストレージコストを算出する

削除候補の未使用テーブルを洗い出す際は、関連するストレージコストもあわせて見えると判断しやすくなります。table_storage_metricsアカウント使用状況ビューを使い、ストレージ単価を1TBあたり月額23ドルと仮定すれば、各テーブルの年間ストレージコストを次のように計算できます。

select
    id as table_id,
    table_catalog || '.' ||table_schema ||'.' || table_name as fully_qualified_table_name,
    (active_bytes + time_travel_bytes + failsafe_bytes + retained_for_clone_bytes)/power(1024,4) as total_storage_tb,
    -- ストレージ単価は$23/TB/月と仮定
    -- ご利用のSnowflake契約に応じて適切な値に変更してください
    total_storage_tb*12*23 as annualized_storage_cost
from snowflake.account_usage.table_storage_metrics
where
    not deleted

過去X日間クエリされていないテーブルをまとめて特定する

ここまでで、テーブルが最後にアクセスされた日時の調べ方と、各テーブルのストレージコストの算出方法を見てきました。これらを組み合わせれば、過去90日間クエリされていないテーブルをすべて洗い出し、削除した場合に見込まれる年間削減額まで可視化できます。

以下のSQLは、Enterprise Edition以上のSnowflakeでのみ利用可能なaccount_usage.access_historyビューを使用します。

dbtをお使いの場合は、より高速に動作する代替版のSQLもご検討ください。

with
access_history as (
    select *
    from snowflake.account_usage.access_history
),
access_history_flattened as (
    select
        access_history.query_id,
        access_history.query_start_time,
        access_history.user_name,
        objects_accessed.value:objectId::integer as table_id,
        objects_accessed.value:objectName::text as object_name,
        objects_accessed.value:objectDomain::text as object_domain,
        objects_accessed.value:columns as columns_array

コードを展開

dbtで未使用テーブルを特定する

access_historyビューへのクエリやフラット化は、処理対象のデータ量が膨大になるため、どうしても実行に時間がかかります。テーブルアクセス履歴をより高速に扱うには、当社のオープンソースdbtパッケージdbt_snowflake_monitoringを使って、データをインクリメンタルにマテリアライズする方法をおすすめします。パッケージを導入すれば、未使用テーブルを特定するクエリも一気にシンプルになります。先ほどのコードは、次のように書き換えられます。

with
table_access_summary as (
    select
        table_id,
        max(query_start_time) as last_accessed_at,
        max_by(user_name, query_start_time) as last_accessed_by,
        max_by(query_id, query_start_time) as last_query_id
    from query_base_table_access
    group by 1
),
table_storage_metrics as (
	select
      id as table_id,
      table_catalog || '.' ||table_schema ||'.' || table_name as fully_qualified_table_name,
      (active_bytes + time_travel_bytes + failsafe_bytes + retained_for_clone_bytes)/power(1024,4) as total_storage_tb,

コードを展開

テーブルが最後に更新された日時を特定する

テーブルを削除すべきか判断する際には、そのテーブルがDDLまたはDML操作で最後に更新された日時を把握しておくと役立ちます。以下のクエリでは、tablesアカウント使用状況ビューを使い、過去1週間以内に更新されたテーブルをすべて取得する方法を紹介します。

select
    table_id,
    table_catalog||'.'||table_schema||'.'||table_name as fully_qualified_table_name,
    last_altered as last_altered_at
from snowflake.account_usage.tables
where
    last_altered > current_date - 7

未使用テーブルの削除は、Snowflakeユーザーが取り組めるコスト削減策のひとつにすぎません。SELECTはテーブルのアクセス状況を可視化するだけでなく、さまざまな最適化レコメンデーションを自動で提示します。下記のリンクから、今すぐご利用いただくか、デモをご予約ください。

Ian Whitestone・SELECT共同創業者兼CEO

Ianは、Snowflake向けコスト管理・最適化SaaSプラットフォームSELECTの共同創業者兼CEOです。SELECT創業以前は、ShopifyおよびCapital Oneでフルスタックのデータサイエンス&エンジニアリングチームを6年間率い、Shopifyではデータウェアハウスの最適化とコスト可視化の推進をリードしました。