0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Snowflake ― 等価条件なのにコストが変わる理由

0
Last updated at Posted at 2026-01-30

目次

※ 本検証は 等価条件・IN 条件 を題材としたものであり、
すべてのクエリパターンに当てはまるものではありません。

1. はじめに

Snowflakeは クエリが速い一方で、コストが分かりにくい と感じられることが多いです。
(同じように見えるSQLでも、実行後の スキャン量 や 課金 が想定より増減することがあります)

本記事では、Snowflakeのコストが 何によって増減するのか を、
Query Profile で検証します。

今回の検証で比較するもの

  • 同じSQL
  • 同じデータ
  • 違うのは 「物理配置」だけ

👉 それでも スキャン量・実行時間・コスト に差が出るのかを確認します。

Snowflakeでは、一般的なRDBの「インデックス有無」ではなく、
データがどう配置されているか(クラスタリング / マイクロパーティション) が
スキャン量や処理効率に影響しやすい、という前提で話を進めます。

⬆ 目次へ戻る

2. Snowflakeのコスト構造

Snowflakeのコストは、大きく 次の2つ に分かれます。

① ストレージコスト

(データを 置いておくだけ の料金)

  • データを保存している量に対する料金
  • 圧縮後サイズ が課金基準
  • クエリを実行しても 基本的に増えない

Snowflakeは高い圧縮率を前提としており、
データ量が増えてもストレージコストは比較的読みやすい設計です。

② コンピュートコスト(Virtual Warehouse)

(動かした分だけ 発生する料金)

  • クエリを実行している 間だけ 発生
  • Virtual Warehouse(計算用エンジン) の使用料
  • Warehouseサイズが大きいほど 時間単価が高い

👉 今回の検証では、この「コンピュートコスト」に注目します


コンピュートコストの考え方

コンピュートコストは、ざっくり以下の関係で決まります。

コンピュートコスト ≒ Warehouseサイズ × 実行時間

  • Warehouseサイズ
    → 計算機の「大きさ・馬力」
  • 実行時間
    → 計算機を「動かしていた時間」

実行時間を決める最大要因

スキャン量(Bytes scanned)

  • どれだけデータを読んだか
  • 読む量が多いほど、実行時間は長くなる

プルーニングの効き具合

  • 不要なデータを 最初から読まない 仕組み
  • 効くほど スキャン量が減る
  • 効かないと ほぼ全データを読む

Snowflakeでは
「同じSQLでも、どれだけスキャンしたか」=コストに直結 します。
この違いを生む要因が、後述する 物理配置 です。

⬆ 目次へ戻る

3. Query Profileとは何か

Query Profileとは

Query Profile とは、クエリ実行後に確認できる
実行結果の可視化(プロファイリング)画面 です。

  • クエリが どの処理を通り
  • どこで時間やリソースを使ったか を
  • 実測ベースで確認できます

Snowflakeにおける位置づけ

Snowflakeでは、
一般的なRDBのように 「事前に作られる実行計画」 はあまり重視されません。

  • クエリ実行前に最適な計画を固定する、という考え方ではない
  • 実行後の結果(実測) をもとに挙動を確認する

👉 この役割を担うのが Query Profile
👉 Query Profile が Snowflake における「実行計画」に相当


Query Profileで分かること

Query Profileを見ることで、以下が確認できます。

  • クエリ全体の 実行時間
  • 各処理(Scan / Join / Aggregate など)の 内訳
  • データの読み取り量 や 処理負荷

Snowflakeでは
「どう実行されたか」を実測で理解する ことが重要です。


Query Profileで見るポイント

Table Scan

  • Partitions scanned / total
    • どれだけのパーティションを読んだか
  • Bytes scanned
    • 実際に読み取ったデータ量

👉 スキャン量が、実行時間・コストに直結


各処理の実行時間

  • Scan / Join / Aggregate など
  • どの処理で時間を使ったか を確認

👉 コスト増加の原因を特定できる

「SQLが同じ」でも、
Scan量や処理配分が違えば、コストは変わる
これを可視化できるのが Query Profile です。

⬆ 目次へ戻る

4. なぜ等価条件でコスト差が出るのか

Snowflakeは「行」ではなく「micro-partition」でデータを読む

Snowflakeでは、
RDBのように 1行ずつ条件判定をしながら読む 仕組みではありません。

  • データは自動的に
    micro-partition と呼ばれる 物理的なデータの塊 に分割されて保存される
  • クエリ実行時は
    micro-partition 単位 で「読む/読まない」を判断する

micro-partition が持つメタデータ

各 micro-partition には、次のような 要約情報(メタデータ) が保持されています。

  • 最小値(MIN)
  • 最大値(MAX)
  • NULL の有無 など

👉 実データを読まずに、中身の傾向を把握できる


読む/読まないの判断基準(プルーニング)

クエリの条件を見て、

  • 「この partition に、条件の値が 入り得るか?」

を判定します。

  • 入り得ない と判断された partition は
    👉 最初から読み飛ばされる(プルーニング)

ポイント
行を見に行く前に、
不要なデータを除外できる のが Snowflake の特徴です。


等価条件でも差が出る理由

同じ 等価条件(= や IN) であっても、
値の分布状態 によってプルーニングの効き方が変わります。


パターン①:値がまとまっている(プルーニングが効く)

  • 同じ値が 限られた partition に集中
  • 条件に合わない partition を 丸ごとスキップ

👉 読む partition が少ない
👉 スキャン量が小さい
👉 コストが低い


パターン②:値が散らばっている(プルーニングが効かない)

  • 同じ値が 多くの partition に分散
  • 多くの partition が
    「条件に合う 可能性あり」と判定される

👉 ほぼ全 partition を読む
👉 スキャン量が大きい
👉 コストが高い


重要なまとめ
SQLが同じ・条件も同じ でも、
micro-partition 上の物理配置が違えば、スキャン量が変わる
= 実行時間・コストが変わる

次の章では、
👉 この差を 実際に Query Profile で確認する検証 を行います。

⬆ 目次へ戻る

5. 検証

検証の目的

同じ 等価条件 であっても、 物理配置の違い によって

  • スキャン量
  • 実行時間
  • コスト

に差が出るかを確認します。


検証の前提条件

  • 同じSQL
  • 同じデータ
    • 件数
    • 値の分布
  • 同じWarehouseサイズ
  • 違うのは 「物理配置」だけ

👉 差が出た場合、
原因はプルーニング効率の違い と説明できます。


検証方法と見るポイント

検証方法(概要)

  • 同じ元データから
    配置の異なる2つのテーブル を作成
    • 値がまとまる配置
    • 値が散らばる配置
  • 同じ 等価条件クエリ を実行
  • Query Profile を比較

検証に使用するデータ量

  • 行数:数百万行(約 300万行)
  • micro-partition 数:16
  • データサイズ:数 MB 程度
  • 列構成:
    • 等価条件に使用する列:category(アルファベット 26種類)
    • 日付列:created_at
    • ペイロード列:payload(文字列・サイズ調整用)

検証用テーブルの構成(概要)

  • 同じ元データから作成した 2つのテーブル
  • データ内容・件数・分布は完全に同一
  • 違いは データの物理的な並び順のみ

テーブル構成

  • T_ORDERED
    • category 列で並び替えて作成
    • 同じ値が近くに配置される
  • T_RANDOM
    • ランダム順で作成
    • 同じ値が全体に散らばる

検証に使用するクエリ

① 等価条件(単一値)

  • 等価条件(=)を使用
  • 返却行数の影響を避けるため COUNT(*)
  • キャッシュの影響を避けて実行
SELECT COUNT(*) FROM T_ORDERED WHERE category = 'A';
SELECT COUNT(*) FROM T_RANDOM  WHERE category = 'A';

② 等価条件(複数値 / IN)

  • 等価条件の 複数値指定(IN)
  • 対象データ範囲を 意図的に拡張
  • プルーニング効率が どのように変化するか を確認
  • キャッシュの影響を避けて実行
SELECT COUNT(*) FROM T_ORDERED WHERE category IN ('A','B','C','D','E');
SELECT COUNT(*) FROM T_RANDOM  WHERE category IN ('A','B','C','D','E');

単一値(=)と比べて
「条件に合致する可能性のある範囲」 が広がるため、
micro-partition の判定精度がどの程度変わるかを確認できます。

クリックして展開(検証用SQL一式)

-- 検証テーブルの元となるデータ
CREATE OR REPLACE TABLE BASE_DATA AS
WITH gen AS (
  SELECT
    SEQ4() AS id
  FROM TABLE(GENERATOR(ROWCOUNT => 3000000))
)
SELECT
  id,
  CHR(65 + MOD(id, 26)) AS category,
  DATEADD('day', MOD(id, 365), '2024-01-01'::DATE) AS created_at,
  RPAD(TO_VARCHAR(id), 200, 'x') AS payload
FROM gen;

-- ① 値がまとまる配置(categoryでまとめる)
CREATE OR REPLACE TABLE T_ORDERED AS
SELECT * FROM BASE_DATA
ORDER BY category;

-- ② 値が散らばる配置(ランダム)
CREATE OR REPLACE TABLE T_RANDOM AS
SELECT * FROM BASE_DATA
ORDER BY RANDOM();

-- キャッシュ無効(必要に応じて)
-- ALTER SESSION SET USE_CACHED_RESULT = FALSE;

-- 検証クエリ(等価条件)
SELECT COUNT(*) FROM T_ORDERED WHERE category = 'A';
SELECT COUNT(*) FROM T_RANDOM  WHERE category = 'A';

-- 検証クエリ(等価条件・複数値)
SELECT COUNT(*) FROM T_ORDERED WHERE category IN ('A','B','C','D','E');
SELECT COUNT(*) FROM T_RANDOM WHERE IN ('A','B','C','D','E');

検証結果(等価条件)

  • 順番どおり
SELECT COUNT(*) FROM T_ORDERED WHERE category = 'A';

スクリーンショット 2026-01-29 20.18.27.png

  • ランダム
SELECT COUNT(*) FROM T_RANDOM  WHERE category = 'A';

スクリーンショット 2026-01-29 20.18.38.png


Query Profile(ORDERED)

観察結果

  • Partitions scanned:1 / 16
  • Bytes scanned:0.02 MB
  • 条件値 category = 'A' は
    特定の micro-partition に集中 している

考察

  • micro-partition が持つ MIN / MAX メタデータ により
    多くの partition が 事前にスキップ された
  • 物理的に値がまとまっている場合、
    等価条件でも高いプルーニング効果 が得られる

Query Profile(RANDOM)

観察結果

  • Partitions scanned:16 / 16
  • Bytes scanned:1.76 MB
  • 同じ値が 全体に散らばっている

考察

  • 各 micro-partition に
    category = 'A' が 含まれる可能性あり と判定される
  • MIN / MAX 判定で除外できず、
    全 partition をスキャン する結果となった

📌 まとめ(等価条件の検証結果)

  • 同じ等価条件
  • 同じデータ件数
  • 同じWarehouseサイズ

であっても、

👉 物理配置の違い により
👉 読まれる micro-partition 数が大きく変わる

結果として、

  • スキャン量
  • 実行時間
  • コンピュートコスト

に 明確な差が生じる ことが確認できた。


検証結果(等価条件・複数値 / IN)

  • 順番どおり
SELECT COUNT(*) FROM T_ORDERED WHERE category IN ('A','B','C','D','E');

スクリーンショット 2026-01-29 20.27.35.png

  • ランダム
SELECT COUNT(*) FROM T_RANDOM WHERE IN ('A','B','C','D','E');

スクリーンショット 2026-01-29 20.27.43.png


Query Profile(ORDERED)

観察結果

  • Partitions scanned:4 / 16
  • Bytes scanned:0.15 MB
  • 対象行数:約 576.9k

考察

  • category が 物理的にまとまって配置 されているため、
    IN 条件でも必要な micro-partition のみをスキャン
  • 単一値(= 'A')と比べるとスキャン範囲は増えるが、
    全体スキャンにはならない

👉 条件が広がっても、プルーニングは段階的に効く


Query Profile(RANDOM)

観察結果

  • Partitions scanned:16 / 16
  • Bytes scanned:1.76 MB
  • 対象行数:約 576.9k

考察

  • 複数値が 全体に散らばっている ため、
    すべての micro-partition に 該当する可能性あり
  • MIN / MAX メタデータで除外できず、
    全 partition をスキャン

👉 IN 条件により、差がさらに明確化


📌 まとめ(IN 条件の検証結果)

  • IN 条件で対象が増えても
    • 物理的にまとまっていれば
      👉 プルーニングは段階的に効く
    • 値が散らばっている場合
      👉 最初から全スキャンになる

結果として、
等価条件・複数値指定では、物理配置の影響がより顕著に表れる

6. 実務でどう生かすか

よく使う絞り込み条件を意識する

実務では、次のような 頻繁に使う絞り込み条件 をまず意識します。

  • 等価条件・IN 条件でよく使う列
    • 種別
    • ステータス
    • 日付 など

これらの列が
物理的に近くに配置されているかどうか で、
micro-partition プルーニングの効き方が大きく変わります。

👉 「WHERE 句で絞れる」よりも
👉 「同じ値が近くに置かれているか」 が効く


Query Profileで「時間ではなく読む量」を見る

実行時間は、次の要因で ブレやすい 指標です。

  • キャッシュの有無
  • Warehouse の混雑状況
  • 同時実行数

そのため実務では、
時間よりも以下を優先して確認 します。

  • Partitions scanned
  • Bytes scanned

同じ結果・同じSQLでも、

  • 読む量が少ない
  • 読む量が多い

では、将来のコスト差 につながります。

👉 速さより「どれだけ読んだか」


「今は速いからOK」は危険

データ量が少ないうちは、
物理配置の差が 表に出にくい ことがあります。

しかし将来、データが増えると…

  • 無駄な 全スキャン
  • クレジット消費の増加
  • 並列実行時の Warehouse 競合

といった問題に発展しやすくなります。

👉 Query Profile を見て、早めに気づけるか が重要


  • SQL の書き方だけでなく
  • 「データがどう置かれているか」
  • 「実際にどれだけ読んでいるか」
    を Query Profile で確認することで、
    将来のコスト増加を未然に防げる。

⬆ 目次へ戻る

7. まとめ

今回分かったこと

  • 同じ等価条件・同じデータ であっても、
    物理配置の違い によって
    👉 読まれる micro-partition 数が大きく変わる
  • 読む量(Partitions / Bytes)が変わることで、
    👉 将来的なコスト差 につながる

Snowflakeらしい見方

  • Snowflakeでは
    実行時間よりも「どれだけ読んだか」 が重要
  • その判断は、
    Query Profile を見ることで説明できる

実務への持ち帰り

  • よく使う 等価条件・IN 条件 が
    効きやすい物理配置 になっているかを意識する
  • 「今は速いからOK」 にせず、
    Query Profile で読む量を確認 する

📌 最後に

Snowflakeでは、Query Profile を通して
「読む量」を意識することが、
コスト最適化の第一歩。

⬆ 目次へ戻る

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?