❄️

Snowflakeコストのブラックボックス化を解消!ACCOUNT_USAGEで作る監視ダッシュボードを作ってみた

に公開

😱 Snowflakeのコスト、本当に把握できていますか?

Snowflakeを運用している皆さん。

こんな悩みを抱えていませんか?

  • 「今月、Snowflakeのクレジット消費が思ったより多い気がする…」
  • 「どのウェアハウスが一番コストを使っているのか、パッと答えられない」
  • 「自動クラスタリングやSnowpipeに、一体いくらかかっているんだろう?」
  • 「ユーザーから『最近クエリが遅い』と報告があったけど、どのクエリが原因か特定するのが大変…」
  • 「メモリ不足(ディスクスピル)やキュー(待機)が多発しているウェアハウスを特定したい」

Snowflakeは非常に強力なデータプラットフォームですが、
その柔軟さゆえに、コストやパフォーマンスの管理が「ブラックボックス化」してしまうことも少なくありませんよね。

Snowsightの標準ダッシュボードも便利ですが、
「もっと自分たちの運用に合わせて、詳細にドリルダウンしたい!」
「独自のKPIを定点観測したい!」
と思うことも多いはずです。

実は、Snowflakeには、これらの悩みを解決するための強力な武器が標準で備わっています。

それが、SNOWFLAKE.ACCOUNT_USAGE スキーマです。

この記事では、ACCOUNT_USAGE スキーマの主要なビューを活用し、
「コスト(自動サービス含む)」「パフォーマンス(ディスクスピル・キュー含む)」「ガバナンス」 を監視するための具体的なSQLクエリと、
それを Streamlit などでダッシュボード化する実践的な第一歩を紹介します。

🗺️ 1. ACCOUNT_USAGE スキーマとは?

https://zenn.dev/yujmatsu/articles/20251109_sf_account_usage

上記の記事でも詳細は記載していますので復習になりますが、ACCOUNT_USAGE スキーマは、SNOWFLAKE という特別な共有データベース内に存在する、読み取り専用のスキーマです。
(すべてのエディション(Standard以上)で利用可能ですが、一部ビュー(例:ACCESS_HISTORY)はEnterprise 以上で利用ができます。)

ここには、あなたのアカウントで行われたほぼ全ての操作履歴(コスト、クエリ実行、ログイン、データロードなど)が蓄積されています。

INFORMATION_SCHEMA との違い

よく似たものに INFORMATION_SCHEMA がありますが、役割が異なります。

比較軸 INFORMATION_SCHEMA ACCOUNT_USAGE
目的 リアルタイムのメタデータ照会 アカウント全体履歴データ分析
データ範囲 各データベース/スキーマ配下 アカウント全体
遅延 ほぼなし ビューにより異なる(最大数時間)
保持期間 短い (7日/14日など) 長い (最大365日)
主な用途 オブジェクトの存在確認、動的SQL コスト分析、パフォーマンス監視、監査

🔐 2. 安全なアクセス権限(RBAC)の設定

ACCOUNT_USAGE は強力ですが、アカウント全体のクエリ履歴やコスト情報など、機密情報を含みます。
ACCOUNTADMIN ロールで直接ダッシュボードを動かすのは絶対に避けるべきです。

ダッシュボード構築の前に、最小権限の原則に基づき、専用のロールを作成しましょう。

推奨ロール設定(ACCOUNTADMINで実行)

Snowflake が提供する「データベースロール」を活用するのがベストプラクティスです。

-- 1. ACCOUNTADMIN になる
USE ROLE ACCOUNTADMIN;

-- 2. ダッシュボード閲覧用のカスタムロールを作成
CREATE ROLE IF NOT EXISTS DASHBOARD_VIEWER_ROLE;

-- 3. 必要な SNOWFLAKE データベースロールをカスタムロールに付与
-- (目的別に最小権限を合成する)

-- コスト/使用量系 (例: WAREHOUSE_METERING_HISTORY, PIPE_USAGE_HISTORY)
GRANT DATABASE ROLE SNOWFLAKE.USAGE_VIEWER TO ROLE DASHBOARD_VIEWER_ROLE;

-- クエリ履歴/アクセス監査 (例: QUERY_HISTORY, ACCESS_HISTORY, TASK_HISTORY)
GRANT DATABASE ROLE SNOWFLAKE.GOVERNANCE_VIEWER TO ROLE DASHBOARD_VIEWER_ROLE;

-- ログイン監査 (例: LOGIN_HISTORY)
GRANT DATABASE ROLE SNOWFLAKE.SECURITY_VIEWER TO ROLE DASHBOARD_VIEWER_ROLE;

-- 4. このロールを、ダッシュボード実行用のユーザー(例: STREAMLIT_USER)に付与
-- (このユーザーの認証情報を使って Streamlit から接続します)
GRANT ROLE DASHBOARD_VIEWER_ROLE TO USER STREAMLIT_USER;

これで、STREAMLIT_USER(と DASHBOARD_VIEWER_ROLE)は、監視に必要なビューに読み取り専用でアクセスできる、安全な実行環境が整いました。

👍 3. 良いダッシュボード vs 悪いダッシュボード

SQLの前に、どのようなダッシュボードを目指すべきか、目的を明確にしておきましょう。

良いダッシュボード 🙆

  • アクションに繋がる:
    • 「どのウェアハウスがコストを使いすぎているか」が一目でわかり、「誰に確認すべきか(例: USER_NAME)」までドリルダウンできる。
  • 異常値が目立つ:
    • 平常時と比べて「実行時間が急に悪化したクエリ」や「エラーが急増した」、「ディスクスピル (Spill) が多発している」ことがハイライトされる。
  • 役割分担が明確:
    • 「インフラ管理者向け(ウェアハウス負荷、キュー監視)」と「チームリーダー向け(プロジェクト別コスト、ユーザー別実行時間)」のように、見る人に合わせてページが分かれている。

悪いダッシュボード(アンチパターン)🙅

  • 単なる「表」の羅列:
    • QUERY_HISTORY をそのまま表示するだけ。情報が多すぎて、どこを見ればいいか分からない。
  • リアルタイム性の誤解:
    • ACCOUNT_USAGE の遅延(最大数時間)を考慮せず、「直近5分のエラー」を表示しようとする。(※これは INFORMATION_SCHEMA の役割です)
  • 「So What?(だから何?)」なグラフ:
    • 「月曜はクエリが多い」というグラフだけあっても、次のアクション(例: 月曜のWHサイズを上げるべきか?)の判断材料がなければ意味がありません。

🛠️ 4. 【SQLクエリ集】ダッシュボード・レシピ

DASHBOARD_VIEWER_ROLE で実行可能な、SQLクエリを以下に紹介します。

1. コスト監視 (主に USAGE_VIEWER)

📊 ウェアハウス別:日次のクレジット消費量

(必要なロール: SNOWFLAKE.USAGE_VIEWER)
「どのウェアハウスがコストを食っているのか?」を一目で把握するための基本クエリです。

-- ウェアハウス別:日次のクレジット消費量 (直近30日)
SELECT
    WAREHOUSE_NAME,
    DATE(START_TIME) AS USAGE_DATE,
    SUM(CREDITS_USED) AS TOTAL_CREDITS_USED
FROM
    SNOWFLAKE.ACCOUNT_USAGE.WAREHOUSE_METERING_HISTORY
WHERE
    START_TIME >= DATEADD(day, -30, CURRENT_TIMESTAMP())
GROUP BY
    WAREHOUSE_NAME,
    USAGE_DATE
ORDER BY
    USAGE_DATE DESC, TOTAL_CREDITS_USED DESC;

☁️ Cloud Services の割合(WH別)

(必要なロール: SNOWFLAKE.USAGE_VIEWER)
Cloud Services が高止まりしていないか把握します(高い場合はメタデータ操作過多やサービス系処理の負荷の可能性)。

-- ※ Cloud Services 列(CREDITS_USED_CLOUD_SERVICES)は最大 6 時間遅延する可能性があります
SELECT
  WAREHOUSE_NAME,
  DATE_TRUNC('day', START_TIME) AS USAGE_DATE,
  SUM(CREDITS_USED)                            AS CREDITS,
  SUM(CREDITS_USED_CLOUD_SERVICES)             AS CS_CREDITS,
  IFF(SUM(CREDITS_USED)=0, NULL,
      SUM(CREDITS_USED_CLOUD_SERVICES)/SUM(CREDITS_USED)) AS CS_RATIO
FROM SNOWFLAKE.ACCOUNT_USAGE.WAREHOUSE_METERING_HISTORY
WHERE START_TIME >= DATEADD(day, -30, CURRENT_TIMESTAMP())
GROUP BY 1,2
ORDER BY USAGE_DATE DESC, CS_RATIO DESC;

📈 日別クレジット vs 7日移動平均

(必要なロール: SNOWFLAKE.USAGE_VIEWER)
コストの急増や異常値を簡易的に検知します。DELTA_FROM_MA7 が大きくプラスに振れた日は要調査です。

WITH BASE AS (
  SELECT DATE(START_TIME) AS USAGE_DATE, WAREHOUSE_NAME,
         SUM(CREDITS_USED) AS CREDITS
  FROM SNOWFLAKE.ACCOUNT_USAGE.WAREHOUSE_METERING_HISTORY
  WHERE START_TIME >= DATEADD(day, -60, CURRENT_TIMESTAMP())
  GROUP BY 1,2
)
SELECT
  WAREHOUSE_NAME, USAGE_DATE, CREDITS,
  AVG(CREDITS) OVER (
    PARTITION BY WAREHOUSE_NAME ORDER BY USAGE_DATE
    ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
  ) AS MA7,
  CREDITS - MA7 AS DELTA_FROM_MA7
FROM BASE
ORDER BY USAGE_DATE DESC, ABS(DELTA_FROM_MA7) DESC;

✈️ データ転送(Egress)コスト

(必要なロール: SNOWFLAKE.USAGE_VIEWER)
Snowflakeから外部リージョンや外部クラウドへのデータ転送(Egress)にかかったコストを監視します。

-- データ転送量 (日次・転送先別) (直近30日)
SELECT
    DATE(START_TIME) AS USAGE_DATE,
    TARGET_CLOUD,
    TARGET_REGION,
    SUM(BYTES_TRANSFERRED) / POWER(1024, 3) AS GB_TRANSFERRED
FROM
    SNOWFLAKE.ACCOUNT_USAGE.DATA_TRANSFER_HISTORY
WHERE
    START_TIME >= DATEADD(day, -30, CURRENT_TIMESTAMP())
    AND TRANSFER_TYPE = 'EGRESS' -- EGRESS (Snowflake -> 外部) のみ
GROUP BY
    USAGE_DATE, TARGET_CLOUD, TARGET_REGION
ORDER BY
    USAGE_DATE DESC;

🤖 自動サービス別:日次のクレジット消費量

(必要なロール: SNOWFLAKE.USAGE_VIEWER)
ウェアハウス(手動)だけでなく、自動化機能にいくらかかっているか把握します。

-- 自動クラスタリング (AC)
SELECT
    SCHEMA_NAME || '.' || TABLE_NAME AS OBJECT,
    SUM(CREDITS_USED) AS CREDITS
FROM 
    SNOWFLAKE.ACCOUNT_USAGE.AUTOMATIC_CLUSTERING_HISTORY
WHERE START_TIME >= DATEADD(day, -30, CURRENT_TIMESTAMP())
GROUP BY 1 ORDER BY CREDITS DESC;

-- 検索最適化 (SOS)
SELECT
  SCHEMA_NAME || '.' || TABLE_NAME AS OBJECT,
  SUM(CREDITS_USED) AS CREDITS
FROM SNOWFLAKE.ACCOUNT_USAGE.SEARCH_OPTIMIZATION_HISTORY
WHERE START_TIME >= DATEADD(day, -30, CURRENT_TIMESTAMP())
GROUP BY 1 ORDER BY CREDITS DESC;

-- マテリアライズドビュー (MV) のリフレッシュコスト
SELECT
  DATABASE_NAME || '.' || SCHEMA_NAME || '.' || TABLE_NAME AS MV,
  SUM(CREDITS_USED) AS CREDITS
FROM SNOWFLAKE.ACCOUNT_USAGE.MATERIALIZED_VIEW_REFRESH_HISTORY
WHERE START_TIME >= DATEADD(day, -30, CURRENT_TIMESTAMP())
GROUP BY 1 ORDER BY CREDITS DESC;

-- Snowpipe
SELECT
  PIPE_NAME,
  DATE_TRUNC('day', START_TIME) AS DAY,
  SUM(BYTES_INSERTED)/POWER(1024,3) AS GB_LOADED,
  SUM(CREDITS_USED) AS CREDITS
FROM SNOWFLAKE.ACCOUNT_USAGE.PIPE_USAGE_HISTORY
WHERE START_TIME >= DATEADD(day, -30, CURRENT_TIMESTAMP())
GROUP BY 1,2 ORDER BY CREDITS DESC;

🗄️ テーブル別ストレージ使用量 Top10

(必要なロール: SNOWFLAKE.USAGE_VIEWER)
Time Travel や Failsafe の使用量も分かります。
TABLE_STORAGE_METRICS ビューの遅延は最大90分程度です。

-- テーブル別ストレージ使用量 Top10 (アクティブバイト順)
SELECT
    TABLE_CATALOG AS DATABASE_NAME,
    TABLE_SCHEMA AS SCHEMA_NAME,
    TABLE_NAME,
    (ACTIVE_BYTES / POWER(1024, 3)) AS ACTIVE_GB,
    (TIME_TRAVEL_BYTES / POWER(1024, 3)) AS TIME_TRAVEL_GB,
    (FAILSAFE_BYTES / POWER(1024, 3)) AS FAILSAFE_GB
FROM
    SNOWFLAKE.ACCOUNT_USAGE.TABLE_STORAGE_METRICS
ORDER BY
    ACTIVE_GB DESC
LIMIT 10;

2. パフォーマンス & 異常検知 (GOVERNANCE_VIEWER)

🐢 実行時間が長いクエリ Top10

「遅い」と言われたら、まずここを見ましょう。
QUERY_HISTORY ビューの遅延は通常45分以内です。

-- 実行時間が長いクエリ Top10 (直近7日)
SELECT
    QUERY_ID, USER_NAME, ROLE_NAME, WAREHOUSE_NAME,
    QUERY_TEXT,
    TOTAL_ELAPSED_TIME / 1000 AS ELAPSED_SECONDS,
    START_TIME
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
WHERE START_TIME >= DATEADD(day, -7, CURRENT_TIMESTAMP())
ORDER BY TOTAL_ELAPSED_TIME DESC
LIMIT 10;

💾 ディスクスピル (Spill) が多いクエリ Top10

メモリ不足(RAM)で処理しきれず、ディスクにデータが溢れた(ディスクスピル/Spill)クエリです。WHサイズのスケールアップ(M→Lなど)を検討するサインです。

-- リモートスピルが多いクエリ (直近7日)
SELECT
  QUERY_ID, USER_NAME, WAREHOUSE_NAME,
  TOTAL_ELAPSED_TIME/1000 AS SECS,
  BYTES_SPILLED_TO_LOCAL_STORAGE  AS SPILL_LOCAL_BYTES,
  BYTES_SPILLED_TO_REMOTE_STORAGE AS SPILL_REMOTE_BYTES,
  START_TIME, QUERY_TEXT
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
WHERE START_TIME >= DATEADD(day, -7, CURRENT_TIMESTAMP())
  AND (BYTES_SPILLED_TO_LOCAL_STORAGE > 0 OR BYTES_SPILLED_TO_REMOTE_STORAGE > 0)
ORDER BY SPILL_REMOTE_BYTES DESC, SPILL_LOCAL_BYTES DESC
LIMIT 20;

📉 プルーニング効率が悪いクエリ Top50

スキャンするパーティションを絞り込めていない(PRUNE_RATIO が高い)、非効率なクエリです。クラスタリングキーの見直し候補です。

-- プルーニング効率が悪いクエリ (直近7日)
SELECT
  QUERY_ID, USER_NAME, WAREHOUSE_NAME,
  PARTITIONS_SCANNED, PARTITIONS_TOTAL,
  IFF(PARTITIONS_TOTAL=0, NULL, PARTITIONS_SCANNED/PARTITIONS_TOTAL) AS PRUNE_RATIO,
  START_TIME, QUERY_TEXT
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
WHERE START_TIME >= DATEADD(day, -7, CURRENT_TIMESTAMP())
  AND PARTITIONS_TOTAL > 100 -- 一定以上のパーティションを持つテーブルへのクエリのみ
ORDER BY PRUNE_RATIO DESC
LIMIT 50;

🧊 キャッシュ効率が低いクエリ Top50

PERCENTAGE_SCANNED_FROM_CACHE が 0% に近いほど、キャッシュが効いていない(=都度ストレージから読んでいる)ことを示します。

-- キャッシュ効率が低いクエリ (直近7日)
SELECT
  QUERY_ID, USER_NAME, WAREHOUSE_NAME,
  PERCENTAGE_SCANNED_FROM_CACHE, BYTES_SCANNED,
  TOTAL_ELAPSED_TIME/1000 AS SECS, START_TIME, QUERY_TEXT
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
WHERE START_TIME >= DATEADD(day, -7, CURRENT_TIMESTAMP())
  AND PERCENTAGE_SCANNED_FROM_CACHE IS NOT NULL
ORDER BY PERCENTAGE_SCANNED_FROM_CACHE ASC, BYTES_SCANNED DESC
LIMIT 50;

🚫 失敗したタスク一覧

(必要なロール: SNOWFLAKE.GOVERNANCE_VIEWER)
定期実行ジョブ(タスク)の失敗を監視します。

-- タスクの失敗率 (直近14日)
SELECT
    DATABASE_NAME || '.' || SCHEMA_NAME || '.' || NAME AS FULL_TASK_NAME, -- TASK_NAME を修正
    DATE_TRUNC('day', SCHEDULED_TIME) AS DAY,
    COUNT(*) AS RUNS,
    COUNT_IF(STATE = 'FAILED') AS FAILS,
    IFF(COUNT(*) = 0, NULL, COUNT_IF(STATE = 'FAILED') / COUNT(*)::FLOAT) AS FAIL_RATE
FROM SNOWFLAKE.ACCOUNT_USAGE.TASK_HISTORY
WHERE SCHEDULED_TIME >= DATEADD(day, -14, CURRENT_TIMESTAMP())
GROUP BY 1, 2
HAVING COUNT_IF(STATE = 'FAILED') > 0
ORDER BY FAIL_RATE DESC, RUNS DESC;

3. ウェアハウス健全性 & 混雑

🚦 時間帯別のキュー比率(混雑状況)

(必要なロール: SNOWFLAKE.USAGE_VIEWER)
恒常的に詰まる時間帯を特定。QUEUE_RATIO が高いならマルチクラスター化やスケジューリング見直しを検討します。
WAREHOUSE_LOAD_HISTORY ビューの遅延は最大180分(3時間) です。

WITH H AS (
  SELECT
    WAREHOUSE_NAME,
    DATE_TRUNC('hour', START_TIME) AS HOUR_BUCKET,
    AVG(AVG_RUNNING)             AS RUNNING,
    AVG(AVG_QUEUED_LOAD)         AS Q_LOAD,
    AVG(AVG_QUEUED_PROVISIONING) AS Q_PROV
  FROM SNOWFLAKE.ACCOUNT_USAGE.WAREHOUSE_LOAD_HISTORY
  WHERE START_TIME >= DATEADD(day, -7, CURRENT_TIMESTAMP())
  GROUP BY 1,2
)
SELECT
  WAREHOUSE_NAME, HOUR_BUCKET, RUNNING, Q_LOAD, Q_PROV,
  IFF(RUNNING+Q_LOAD=0, 0, Q_LOAD/(RUNNING+Q_LOAD)) AS QUEUE_RATIO
FROM H
ORDER BY HOUR_BUCKET DESC, QUEUE_RATIO DESC;

🕒 "Cold Start" 待機が長いクエリ

(必要なロール: SNOWFLAKE.GOVERNANCE_VIEWER)
ウェアハウスの起動(レジューム)待ちが長かったクエリです。自動サスペンドの時間を長くするか、ピーク時間は起動し続ける運用を検討します。

-- プロビジョニング待ちが長いクエリ (直近7日)
SELECT
  QUERY_ID, USER_NAME, WAREHOUSE_NAME,
  QUEUED_PROVISIONING_TIME/1000 AS QUEUED_PROV_SECS,
  START_TIME, QUERY_TEXT
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
WHERE START_TIME >= DATEADD(day, -7, CURRENT_TIMESTAMP())
  AND QUEUED_PROVISIONING_TIME > 0
ORDER BY QUEUED_PROVISIONING_TIME DESC
LIMIT 50;

🔄 WHイベント(自動サスペンド/レジューム)

(必要なロール: SNOWFLAKE.USAGE_VIEWER)
ウェアハウスが意図通りに停止・再開しているか、リサイズが頻発していないかを確認します。

-- WHイベントの日別集計 (直近30日)
SELECT
  WAREHOUSE_NAME,
  DATE_TRUNC('day', TIMESTAMP) AS DAY,
  SUM(IFF(EVENT_NAME='SUSPEND_WAREHOUSE',1,0)) AS SUSPENDS,
  SUM(IFF(EVENT_NAME='RESUME_WAREHOUSE', 1,0)) AS RESUMES,
  SUM(IFF(EVENT_NAME='RESIZE_WAREHOUSE', 1,0)) AS RESIZES
FROM SNOWFLAKE.ACCOUNT_USAGE.WAREHOUSE_EVENTS_HISTORY
WHERE TIMESTAMP >= DATEADD(day, -30, CURRENT_TIMESTAMP())
GROUP BY 1,2
ORDER BY DAY DESC, WAREHOUSE_NAME;

4. セキュリティ & ガバナンス

🔒 ログイン失敗履歴

(必要なロール: SNOWFLAKE.SECURITY_VIEWER)
不正アクセスの試行や、設定ミス(期限切れパスワードなど)を検知します。

-- ログイン失敗履歴 (直近7日)
SELECT
    EVENT_TIMESTAMP,
    USER_NAME,
    CLIENT_IP,
    REPORTED_CLIENT_TYPE,
    ERROR_MESSAGE
FROM
    SNOWFLAKE.ACCOUNT_USAGE.LOGIN_HISTORY
WHERE
    EVENT_TIMESTAMP >= DATEADD(day, -7, CURRENT_TIMESTAMP())
    AND IS_SUCCESS = 'NO'
ORDER BY
    EVENT_TIMESTAMP DESC;

🔭 よく参照されるテーブル Top50

(必要なロール: SNOWFLAKE.GOVERNANCE_VIEWER)
“ホットテーブル”と担当者を把握し、最適化や権限制御の優先順位付けに。
ACCESS_HISTORY は Enterprise Edition 以上が必要です。

-- よく参照されるテーブルと関与ユーザー(直近 7 日)
WITH AH AS (
  SELECT USER_NAME, DIRECT_OBJECTS_ACCESSED, QUERY_START_TIME
  FROM SNOWFLAKE.ACCOUNT_USAGE.ACCESS_HISTORY
  WHERE QUERY_START_TIME >= DATEADD(day, -7, CURRENT_TIMESTAMP())
)
SELECT
  VALUE:objectName::string      AS OBJECT_NAME,
  COUNT(*)                      AS ACCESS_COUNT,
  ARRAY_AGG(DISTINCT USER_NAME) AS USERS
FROM AH, LATERAL FLATTEN(input => DIRECT_OBJECTS_ACCESSED)
WHERE VALUE:objectDomain::string = 'TABLE'
GROUP BY 1
ORDER BY ACCESS_COUNT DESC
LIMIT 50;

5. おまけ:ダッシュボード自体の監視

(必要なロール: SNOWFLAKE.GOVERNANCE_VIEWER)
StreamlitのTIPSで設定した QUERY_TAG を使って、ダッシュボード自体の負荷やエラー率を監視します。

-- ダッシュボード (QUERY_TAG) 別の負荷とエラー率
SELECT
    QUERY_TAG,
    WAREHOUSE_NAME,
    COUNT(*) AS NUM_QUERIES,
    SUM(IFF(ERROR_CODE IS NOT NULL, 1, 0)) AS NUM_ERRORS,
    AVG(TOTAL_ELAPSED_TIME / 1000) AS AVG_DURATION_SEC,
    AVG(BYTES_SPILLED_TO_REMOTE_STORAGE) AS AVG_REMOTE_SPILL_BYTES
FROM
    SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
WHERE
    START_TIME >= DATEADD(day, -7, CURRENT_TIMESTAMP())
    AND QUERY_TAG = 'dash/account-usage/v1' -- Streamlitで設定したタグ
GROUP BY
    QUERY_TAG, WAREHOUSE_NAME;

🚀 5. 実践:ダッシュボードを作成する

さて、強力なSQLが手に入りました。これらを「見える化」しましょう。
用途に応じて2つの方法があります。

パターン1:Snowsight 標準ダッシュボード(手軽さ・グラフ化重視)

「まずは手軽にグラフで見たい」「Pythonコードは書きたくない」という場合は、Snowsight の標準ダッシュボード機能が最適です。

ステップ1: ダッシュボードの作成

  1. Snowsight の左側メニューから「ダッシュボード」を選択します。
  2. 右上の「+ ダッシュボード」をクリックし、新しいダッシュボードを作成します(例: ACCOUNT_USAGE監視)。

ステップ2: SQLクエリからタイルを追加

  1. 作成したダッシュボードで、「+ タイルを追加」をクリックします。
  2. SQLワークシートから新しいタイル」を選択します。
  3. 開いたワークシートに、「セクション5」のSQLクエリ(例: 📊 ウェアハウス別:日次のクレジット消費量)を貼り付けます。
  4. (重要) ワークシートのコンテキスト(ロール)を、セクション3で作成した DASHBOARD_VIEWER_ROLE に設定します。
  5. クエリを実行し、結果が表示されることを確認します。

ステップ3: グラフの選択(棒グラフなど)

  1. ワークシート上部のタブで「グラフ」を選択します。
  2. Snowsight が自動でグラフを提案してくれますが、右側の設定パネルでカスタマイズします。

📈 グラフ選択のヒント(良し悪し)

  • ウェアハウス別の日次消費:
    • 良い例: USAGE_DATE をX軸、TOTAL_CREDITS_USED をY軸にし、「積み上げ棒グラフ」を選択。WAREHOUSE_NAME で色分けします。
    • 理由: 日ごとの総消費量と、どのウェアハウスが主要因かが一目でわかります。
  • 時間帯別のキュー比率:
    • 良い例: HOUR_BUCKET をX軸、WAREHOUSE_NAME をY軸、RUNNING を色にした「ヒートマップ」または「棒グラフ」。
    • 理由: 混雑する「時間帯」と「ウェアハウス」を特定できます。
  • Top10 クエリ(実行時間、ディスクスピルなど):
    • 良い例:テーブル」を選択します。QUERY_TEXT が読めないと意味がないためです。
    • 悪い例: これを棒グラフにしても、「どのクエリか」がわからずアクションに繋がりません。
  • 移動平均との乖離:
    • 良い例(案1): USAGE_DATE をX軸にし、CREDITSMA7 を「折れ線グラフ」で表示する。
    • 良い例(案2): USAGE_DATE をX軸にし、DELTA_FROM_MA7 のみを「棒グラフ」で表示する。
    • 理由: 案1はトレンドとの乖離を、案2は異常値の突出を視覚化できます。Snowsight ではコンボチャートが作れないため、これら2つのタイルをダッシュボードに並べて配置するのが現実的です。

ステップ4: ダッシュボードに追加

グラフを調整したら、右上の「ダッシュボードに追加」をクリックします。
この作業を、監視したいSQLクエリの数だけ繰り返します。

実際に作成したサンプル画面
グラフやデータは表示されていないですがタイルは配置しているのでイメージは伝わるかと思います。

メリット:

  • SQLさえ書ければ素早く可視化 :クエリ結果をグラフ/表タイルとして配置し、複数のクエリを1つのボードに並べられます。

  • 安全な実行モデル :ダッシュボードは閲覧者の“プライマリ・ロールのみ”で実行され、セカンダリ・ロールは無効化されます(意図せぬ過剰権限での実行を回避)。DASHBOARD_VIEWER_ROLE という特別ロールが必須という事実はありません。

  • 共通フィルタでの絞り込み :ダッシュボード横断でフィルタを定義し、複数タイルに効かせられます。SQLの書き換えなしで運用可能。

  • ロール/ウェアハウスの明示 :ダッシュボード単位で実行ロールとウェアハウスを指定可能。コストや権限のコントロールがしやすい。

デメリット:

  • 高度なインタラクションは限定的 :UIはフィルタ中心。スライダーやボタン、自由な状態管理・イベント処理などの 自由度は低い (任意のPythonコードは実行できない)。

  • 外部連携や高度処理は別機能で :外部API呼び出し、Pythonベースの特徴量生成・モデル推論などはダッシュボード単体では対応不可(タスク/UDF/外部関数等の別機構と役割分担)。

パターン2:Streamlit in Snowflake (SiS)(インタラクティブ性・カスタマイズ重視)

「日数スライダーで期間を動的に変えたい」「Pythonでさらに高度な分析を加えたい」場合は、Streamlit in Snowflake (SiS) が最適です。

ステップ1: SiS アプリの作成 (Snowsight UI)

  1. Snowsight (UI) の左側メニューから「Streamlit」を選択します。
  2. + Streamlit アプリ」をクリックします。
  3. アプリ名(例: ACCOUNT_USAGE_DASHBOARD)、アプリ用のウェアハウス(例: STREAMLIT_WH)、アプリが配置されるデータベース/スキーマ(例: MY_TOOLS.DASHBOARDS)を選択し、「作成」をクリックします。
  4. (重要) アプリのオーナー(作成者)のデフォルトロールが、セクション3で作成した DASHBOARD_VIEWER_ROLE を継承している(または、アプリの実行ロールとして DASHBOARD_VIEWER_ROLE を指定する)ことを確認してください。

ステップ2: Python コードの記述

エディタが開いたら、Snowpark のセッションを使ってSQLを実行します。
(※以下のコードはセクション5のレシピ「ウェアハウス別クレジット消費量」を使った最小サンプルです)

import streamlit as st
import pandas as pd
import altair as alt
# SiS では Snowpark のセッションを直接取得します
from snowflake.snowpark.context import get_active_session

# --- Snowflake接続 ---
@st.cache_resource
def get_snowflake_session():
    # SiS は、Snowsight で現在アクティブなセッションを自動的に取得します。
    # このアプリを実行するロールは、SNOWFLAKE DBロール (USAGE_VIEWER等) を
    # 事前に継承している必要があります。
    session = get_active_session()
    # Streamlitからのクエリにタグを付け、QUERY_HISTORYで追跡しやすくする
    session.query_tag = 'dash/account-usage/v1'
    return session

session = get_snowflake_session()

# --- SQLを関数化 ---
# (必要なロール: SNOWFLAKE.USAGE_VIEWER)
# WAREHOUSE_METERING_HISTORY の遅延 (最大180分) を考慮し、
# キャッシュのTTL (Time To Live) を 3時間 (10800秒) に設定
@st.cache_data(ttl=10800) 
def get_warehouse_metering(days=30):
    # データベース名・スキーマ名を明示的に指定
    sql = f"""
    SELECT
        WAREHOUSE_NAME,
        DATE(START_TIME) AS USAGE_DATE,
        SUM(CREDITS_USED) AS TOTAL_CREDITS_USED
    FROM
        SNOWFLAKE.ACCOUNT_USAGE.WAREHOUSE_METERING_HISTORY
    WHERE
        START_TIME >= DATEADD(day, -{days}, CURRENT_TIMESTAMP())
    GROUP BY
        WAREHOUSE_NAME,
        USAGE_DATE
    ORDER BY
        USAGE_DATE, WAREHOUSE_NAME;
    """
    # Snowpark セッションを使って SQL を実行し、Pandas DataFrame に変換
    df = session.sql(sql).to_pandas()
    return df

# --- Streamlit UI構築 ---
st.set_page_config(layout="wide")
st.title("Snowflake コスト&パフォーマンス・ダッシュボード 📊")

# --- 1. コスト監視 ---
st.header("1. ウェアハウス別 クレジット消費")

# 日数を選択するスライダー
days_to_show = st.slider("表示日数を選択 (直近)", 7, 90, 30)

# データ取得
df_metering = get_warehouse_metering(days=days_to_show)

if not df_metering.empty:
    # Altair用にデータを整形
    df_melted = df_metering.melt(
        id_vars=['USAGE_DATE', 'WAREHOUSE_NAME'], 
        value_vars=['TOTAL_CREDITS_USED'],
        var_name='Metric',
        value_name='Credits'
    )

    # 積み上げ棒グラフ
    st.subheader(f"日別・ウェアハウス別クレジット消費 (直近{days_to_show}日)")
    chart = alt.Chart(df_melted).mark_bar().encode(
        x=alt.X('USAGE_DATE', title='日付'),
        y=alt.Y('Credits', title='クレジット消費量'),
        color=alt.Color('WAREHOUSE_NAME', title='ウェアハウス'),
        tooltip=['USAGE_DATE', 'WAREHOUSE_NAME', 'Credits']
    ).interactive()

    st.altair_chart(chart, use_container_width=True)

else:
    st.warning("データを取得できませんでした。")

# TODO: ここに「パフォーマンス監視」や「エラー・リスク監視」のセクションを追加していく...
# (GOVERNANCE_VIEWER / SECURITY_VIEWER が必要)

ステップ3: アプリの実行と共有

  1. コードをエディタに貼り付けたら、右上の「▶ 実行」をクリックします。
  2. アプリが動作することを確認したら、右上の「共有」ボタンから、このダッシュボードを閲覧させたいロール(例: TEAM_LEAD_ROLE)に VIEW 権限を付与します。

実際に作成したサンプル画面

メリット:

  • リッチな双方向UI :スライダー、セレクトボックス、ボタン等のウィジェットで 探索的分析や業務UI を素早く構築。マルチページにも対応。

  • Snowflake内で完結 :Snowsightからそのまま開発・実行でき、使用する ウェアハウスやPythonパッケージ もアプリ設定で管理。

  • 権限のカプセル化(Owner’s rights)アプリはオーナー権限で実行 されるため、閲覧者に直接テーブル権限を配らずに利用させる設計も可能(ただし後述のリスク理解が前提)。

デメリット:

  • 権限設計の難易度 :Owner’s rightsにより、アプリ経由で広い権限が行使され得る ため、オーナーロールの最小権限化・データ制御(ビューやポリシー)設計が重要。
    Snowflake Documentation

  • 環境制約 :利用できるPythonパッケージはSnowflakeが管理するセットに制限。 ネットワーク接続や外部アクセスにもルール・前提があります(プライベート接続の設定など)。
    Snowflake Documentation

  • 学習・運用コスト :Pythonアプリ開発・ライブラリ管理・バージョン整合など、ダッシュボードに比べ初期学習/保守の負荷が高め。

😌 おわりに

ACCOUNT_USAGE スキーマは、Snowflake運用者にとってまさに「宝の山」です。

今回紹介したクエリは、コスト、パフォーマンス、混雑状況、自動化コスト、セキュリティリスクを網羅した、「見るだけで次のアクションが決まる」 監視面の土台となります。

これらを「いつでも見られるダッシュボード」として整備しておくだけで、コストやパフォーマンスに対する解像度は劇的に上がることかと思います。

まずは、DASHBOARD_VIEWER_ROLE を作成し、SQLをコピーしてSnowsightで実行することから始めてみませんか?

そして、StreamlitやLooker Studio、Tableauなど、お好みのツールで、ぜひ「自社専用の監視ダッシュボード」作りに挑戦してみてください。

📚 参考出典

Discussion