CodeGym /コース /SQL SELF /PARTITION BY を使ってデータをグループ分けする方法

PARTITION BY を使ってデータをグループ分けする方法

SQL SELF
レベル 29, レッスン 3
使用可能

例えば、君が大きなレストランでウェイター(もしくはコーヒー好きならバリスタ)として働いているとしよう。毎日チップの合計を計算するんだけど、レストランはエリアごとに分かれていて、各エリアごとにどれだけチップをもらったか知りたい。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 を使うときは注意しよう。

2
タスク
SQL SELF, レベル 29, レッスン 3
ロック未解除
地域ごとの売上合計
地域ごとの売上合計
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION