例えば、君が大きなレストランでウェイター(もしくはコーヒー好きならバリスタ)として働いているとしよう。毎日チップの合計を計算するんだけど、レストランはエリアごとに分かれていて、各エリアごとにどれだけチップをもらったか知りたい。PARTITION BY は、SQLで「レストランをエリアごとに分ける」ためのものなんだ。
もうちょっとフォーマルに言うと、PARTITION BY はウィンドウ関数でテーブルの全行をグループ(または「パーティション」)に分けるために使う。各グループごとにウィンドウ関数が再実行される。つまり、各「パーティション」ごとに関数を個別に適用するイメージだね。
例:どうやって動くの?
例えば、sales という売上データのテーブルがあるとしよう:
| region | salesperson | amount |
|---|---|---|
| North | Alice | 100 |
| North | Bob | 200 |
| South | Alice | 150 |
| South | Charlie | 250 |
もし各営業担当ごとに、でもエリアごとに分けて、どれだけ稼いだか計算したいなら、PARTITION BY が必要だよ。
PARTITION BY の構文
構文はめっちゃシンプル:
ウィンドウ関数() OVER (PARTITION BY カラム_または_カラムたち)
ウィンドウ関数()— 例えばSUM()、AVG()、ROW_NUMBER()など。PARTITION BY カラム— どのカラムで行を分けるか指定する。OVER()— SQLに「このウィンドウ内で何かやってね」と伝える演算子。
例:グループごとの合計
各エリアごとの売上合計を計算してみよう:
SELECT
region,
salesperson,
amount,
SUM(amount) OVER (PARTITION BY region) AS total_sales_by_region
FROM sales;
結果はこんな感じ:
| region | salesperson | amount | total_sales_by_region |
|---|---|---|---|
| North | Alice | 100 | 300 |
| North | Bob | 200 | 300 |
| South | Alice | 150 | 400 |
| South | Charlie | 250 | 400 |
何が起きてる?SQLは region(NorthとSouth)の値ごとに行をグループ分けして、SUM() 関数を各グループごとに実行してる。だから「North」グループの行は全部同じ合計、「South」グループも同じ合計になるんだ。
PARTITION BY の使い方例
実際の現場で PARTITION BY がどう役立つか見てみよう。
例1:グループ内でランキング
例えば、各エリアごとに営業担当を売上順でランク付けしたいとき。PARTITION BY と RANK() を組み合わせて使える:
SELECT
region,
salesperson,
amount,
RANK() OVER (PARTITION BY region ORDER BY amount DESC) AS rank_in_region
FROM sales;
結果:
| region | salesperson | amount | rank_in_region |
|---|---|---|---|
| North | Bob | 200 | 1 |
| North | Alice | 100 | 2 |
| South | Charlie | 250 | 1 |
| South | Alice | 150 | 2 |
RANK() 関数は各 region グループ内で1からランクを付ける。各グループでランクは1から始まるよ。
例2:各値をグループ平均と比較
例えば、各営業担当が自分のエリアの平均と比べてどれだけ売り上げたか見たいとき。AVG() を使う:
SELECT
region,
salesperson,
amount,
AVG(amount) OVER (PARTITION BY region) AS avg_sales_by_region,
amount - AVG(amount) OVER (PARTITION BY region) AS diff_from_avg
FROM sales;
結果:
| region | salesperson | amount | avg_sales_by_region | diff_from_avg |
|---|---|---|---|---|
| North | Alice | 100 | 150 | -50 |
| North | Bob | 200 | 150 | 50 |
| South | Alice | 150 | 200 | -50 |
| South | Charlie | 250 | 200 | 50 |
まずSQLは region ごとに行をグループ分け。次に AVG(amount) で各グループの平均を計算。最後に各行ごとに自分の値と平均の差を出してる。
例3:グループ内で行に番号を振る
例えば、各エリアごとに全トランザクションに番号を振りたいとき。ROW_NUMBER() を使う:
SELECT
region,
salesperson,
amount,
ROW_NUMBER() OVER (PARTITION BY region ORDER BY amount DESC) AS row_number
FROM sales;
結果:
| region | salesperson | amount | row_number |
|---|---|---|---|
| North | Bob | 200 | 1 |
| North | Alice | 100 | 2 |
| South | Charlie | 250 | 1 |
| South | Alice | 150 | 2 |
GROUP BY との比較
PARTITION BY と GROUP BY を混同しがちなので、違いを比べてみよう:
GROUP BY
GROUP BY は出力結果の構造を変える。つまり、テーブルの行を集計値に変換する。例えば:
SELECT
region,
SUM(amount) AS total_sales
FROM sales
GROUP BY region;
結果:
| region | total_sales |
|---|---|
| North | 300 |
| South | 400 |
ここでは営業担当の情報が消えて、データが集計されてしまう。
PARTITION BY
逆に PARTITION BY は構造を変えない。各行はそのまま見えるけど、グループごとの追加計算値が付く。つまり PARTITION BY は詳細を失わずに集計できるんだ。
PARTITION BY でよくあるミス
ミス1:PARTITION BY を忘れる
データをグループ化したいのに PARTITION BY を書き忘れることがある。例えば:
SELECT
region,
salesperson,
amount,
SUM(amount) OVER () AS total_sales
FROM sales;
結果:
| region | salesperson | amount | total_sales |
|---|---|---|---|
| North | Alice | 100 | 700 |
| North | Bob | 200 | 700 |
| South | Alice | 150 | 700 |
| South | Charlie | 250 | 700 |
ここでは SUM(amount) がテーブル全体で計算されてる。エリアごとに集計したいなら PARTITION BY region を忘れずに!
ミス2:ORDER BY の順番ミス
RANK() や ROW_NUMBER() みたいな関数では、ウィンドウ内の行の順番が大事。OVER() の中で ORDER BY を使うときは注意しよう。
GO TO FULL VERSION