パッと見だと、ウィンドウ関数と集約関数ってどっちもデータ分析や処理に使う似たようなツールに見えるよね。どっちも合計や平均、ランキングとかの計算ができる。でも、実は本質的に違うんだ。詳しく見てみよう!
集約関数(GROUP BY)
集約関数はこんな感じで動くよ:
- 指定したカラムで行をグループ化する。
- グループ化した後、各グループは結果の1行になる。
- 例:各地域ごとの売上合計を知りたいとき。
SELECT region, SUM(sales) AS total_sales
FROM sales_data
GROUP BY region;
ポイント: GROUP BYはデータを「圧縮」する。グループ化したら、そのグループに入ってた行は消えて、集計結果だけが残るよ。
ウィンドウ関数(PARTITION BY)
ウィンドウ関数は逆に:
- 元のデータ構造をそのまま保つ(行が消えたり圧縮されたりしない!)。
- 「ウィンドウ」=論理的に区切ったグループ内で計算できる。
例:各都市の売上が、その都市が属する地域の合計売上に対してどれくらいの割合か知りたい。でも全データはそのまま残したい。
SELECT
region,
city,
sales,
SUM(sales) OVER (PARTITION BY region) AS total_sales_by_region
FROM sales_data;
ポイント:ウィンドウ関数を使っても行は消えない。各行に新しい計算結果が追加されるだけ。
例:SUM()とGROUP BY vs SUM()とPARTITION BY
違いをもっとわかりやすくするために、SUM()が両方でどう動くか見てみよう。例えば、sales_dataテーブルがこんな感じだとする:
| region | city | sales |
|---|---|---|
| North | CityA | 100 |
| North | CityB | 150 |
| South | CityC | 200 |
| South | CityD | 250 |
GROUP BYで合計する場合
各地域ごとの売上合計を知りたい:
SELECT region, SUM(sales) AS total_sales
FROM sales_data
GROUP BY region;
結果はこうなる:
| region | total_sales |
|---|---|
| North | 250 |
| South | 450 |
何が起きたか: regionでグループ化されて、各グループが売上合計1行に「圧縮」された。
PARTITION BYで合計する場合
今度はウィンドウ関数で同じことをやってみる:
SELECT
region,
city,
sales,
SUM(sales) OVER (PARTITION BY region) AS total_sales_by_region
FROM sales_data;
結果:
| region | city | sales | total_sales_by_region |
|---|---|---|---|
| North | CityA | 100 | 250 |
| North | CityB | 150 | 250 |
| South | CityC | 200 | 450 |
| South | CityD | 250 | 450 |
何が起きたか: PARTITION BYは行を「圧縮」しなかった。その代わり、特定のウィンドウ内(各地域ごと)で合計を計算した。
GROUP BYとPARTITION BY、どっちを使う?
GROUP BY: 最終レポートに向いてる
GROUP BYはデータ量を減らして、グループごとの最終結果が欲しいときに便利。例えば:
- 月ごとの売上合計。
- 商品カテゴリごとの注文数カウント。
例:
SELECT category, COUNT(*) AS total_orders
FROM orders
GROUP BY category;
PARTITION BY: 分析や詳細チェックに最適
PARTITION BYは、全データを残したまま、各行ごとに追加計算したいときに使う。例えば:
- 各商品のカテゴリ内での売上割合を出す。
- 各グループ内で行に連番を振る。
売上割合の計算例:
SELECT
category,
product,
sales,
ROUND(
(sales * 100.0) / SUM(sales) OVER (PARTITION BY category),
2
) AS sales_percentage
FROM sales_data;
例:複数のウィンドウ関数を使う
ウィンドウ関数の強みの一つは、いろんな計算を一気にできること。例えば:
SELECT
region,
city,
sales,
SUM(sales) OVER (PARTITION BY region) AS total_sales,
RANK() OVER (PARTITION BY region ORDER BY sales DESC) AS sales_rank
FROM sales_data;
結果:
| region | city | sales | total_sales | sales_rank |
|---|---|---|---|---|
| North | CityB | 150 | 250 | 1 |
| North | CityA | 100 | 250 | 2 |
| South | CityD | 250 | 450 | 1 |
| South | CityC | 200 | 450 | 2 |
GROUP BYよりウィンドウ関数が優れてるポイント
元データをそのまま残せる: GROUP BYは行を「圧縮」しちゃうけど、ウィンドウ関数ならテーブルの構造をそのままキープできる。
1つのクエリで複数計算できる: PARTITION BYやORDER BYのパラメータを変えて、いろんなウィンドウ関数を同時に使える。
分析の柔軟性: ウィンドウ関数なら累積合計、ランキング、割合計算など、いろんな分析が自由自在!
柔軟性の例
いくつかの関数を組み合わせてみよう:
SELECT
region,
city,
sales,
SUM(sales) OVER (PARTITION BY region) AS total_sales,
AVG(sales) OVER (PARTITION BY region) AS avg_sales,
RANK() OVER (PARTITION BY region ORDER BY sales DESC) AS rank
FROM sales_data;
結果:
| region | city | sales | total_sales | avg_sales | rank |
|---|---|---|---|---|---|
| North | CityB | 150 | 250 | 125.0 | 1 |
| North | CityA | 100 | 250 | 125.0 | 2 |
| South | CityD | 250 | 450 | 225.0 | 1 |
| South | CityC | 200 | 450 | 225.0 | 2 |
制限とよくあるミス
よくあるミスの一つは、「データを圧縮したいときにPARTITION BYを使っちゃう」こと。例えば、本当はこう書くべきなのに:
SELECT region, SUM(sales) AS total_sales
FROM sales_data
GROUP BY region;
こう書いちゃう人がいる:
SELECT
region,
SUM(sales) OVER (PARTITION BY region) AS total_sales
FROM sales_data;
でもこれだと全行が返ってきて、データ量は減らない(それが欲しい結果じゃない場合も多い)。
これで、GROUP BYとウィンドウ関数、どっちを使うべきかバッチリわかったね。ハンマーとドライバーの使い分けみたいなもんで、どっちも釘は打てるけど…やり方が違うんだ。
GO TO FULL VERSION