1. 「最初の集計ノード」で何が起きていたか
Snowflakeがクエリを実行する際、処理を「ノード(工程)」に分解します。2つ目のクエリでは以下の順で処理が進みます。
- データ読み込み: 3つのテーブルをJOINする。
- 【最初の集計ノード】:
GROUP BY cd_gender, cs_order_numberを実行。←ここで大事件が発生! - 2番目の集計ノード: その結果をさらに
GROUP BY cd_genderでまとめる。
-- ❌ スピル(メモリ不足)が発生するクエリ
SELECT
cd_gender,
AVG(lp) average_list_price,
AVG(sp) average_sales_price,
AVG(qu) average_quantity
FROM (
-- 【ここが問題!】サブクエリ
SELECT
cd_gender,
cs_order_number, -- 👈 注文番号(数百万〜数千万種類ある)
AVG(cs_list_price) lp,
AVG(cs_sales_price) sp,
AVG(cs_quantity) qu
FROM catalog_sales, date_dim, customer_demographics
WHERE cs_sold_date_sk = d_date_sk
AND cs_bill_cdemo_sk = cd_demo_sk
AND d_year = 2000
GROUP BY
cd_gender,
cs_order_number -- 👈 ここで「性別×注文番号」の数千万個のバケツを作ろうとしてメモリがパンク!
) inner_query
GROUP BY
cd_gender; -- 最後に数個(性別)にまとめているが、前の工程ですでにメモリが溢れているなぜここでスピルするのか?
cs_order_number(注文番号)は非常に種類が多いため、Snowflakeはこの「最初の集計ノード」で、数百万〜数千万件のユニークな組み合わせをメモリ上に保持しようとします。 しかし、ウェアハウスのメモリ容量を超えてしまうため、データがディスクに溢れ(スピル)、処理が極端に遅くなっていました。
2. 「書き直し」による修正の正体
説明文にある「cs_order_number を削除して実行するだけ」という修正は、**「最初の集計ノードでの負担を 100万分の1 以下にする」**という劇的な改善策です。
- 修正前: 数千万件の「注文 × 性別」のペアを管理する。
- 修正後: 数件の「性別」のグループだけを管理する。
-- ✅ メモリ内で完結し、高速に終わるクエリ
SELECT
cd_gender, -- 👈 最初から「性別」だけを見る
AVG(cs_list_price) lp,
AVG(cs_sales_price) sp,
AVG(cs_quantity) qu
FROM catalog_sales, date_dim, customer_demographics
WHERE cs_sold_date_sk = d_date_sk
AND cs_bill_cdemo_sk = cd_demo_sk
AND d_year = 2000
GROUP BY
cd_gender; -- 👈 グループは「男・女・不明」の数個だけ。メモリに余裕で収まる!これにより、すべての処理がメモリ内で完結(インメモリ処理)できるようになり、スピルが解消されるため、実行時間が半分(あるいはそれ以上)に短縮されるのです。
3. この事例から学べる「Snowflake運用の鉄則」
この説明文が教えてくれている最も重要な教訓は以下の通りです。
- 不必要なグループ化を避ける: 最終的に「性別ごと」の集計が見たいだけなら、途中で「注文番号ごと」に細かく分ける必要はありません。
- サブクエリ(ネスト)の罠: 「一度中間集計したほうが速くなるかも?」という良かれと思った工夫が、分散データベースでは逆に「データ移動とメモリ消費」を増やして仇となることがあります。
- 集計ノードを意識する: クエリプロファイルを見て「Aggregate(集計)」のステップで時間がかかっていたり、スピルが発生していたら、**「そのグループ化、本当にその粒度(細かさ)でやる必要がある?」**と疑うのが高速化の近道です。



