LESSON 30分

ウィンドウ関数入門

ストーリー

「部署内での給与ランキングを出してほしいんだけど」

「GROUP BY で部署ごとに集計すると...あれ、個々の社員の行が消えちゃいます」

「そう、GROUP BY は行をまとめてしまうからね。行を残したまま集計したい場合は、ウィンドウ関数を使うんだ」

「ウィンドウ関数?」

「SQLの中でも特に強力な機能だ。これが使えると『できるエンジニア』って言われるぞ」


ウィンドウ関数とは

ウィンドウ関数は、行をグループ化せずに、行ごとに集計計算を行う関数です。

GROUP BY との違い

GROUP BY:     行をまとめる → 結果の行数が減る
ウィンドウ関数: 行はそのまま → 各行に集計結果を付与

具体例

sql
-- GROUP BY: 部署ごとの平均給与(行がまとまる)
SELECT department_id, AVG(salary) FROM employees GROUP BY department_id;
-- → 4行(部署の数)

-- ウィンドウ関数: 各社員の行に部署平均を付与(行はそのまま)
SELECT name, salary, AVG(salary) OVER (PARTITION BY department_id) AS 部署平均
FROM employees;
-- → 10行(社員の数のまま)

基本構文

sql
関数名() OVER (
    PARTITION BY グループ化するカラム
    ORDER BY 並び替えるカラム
)
要素説明省略
PARTITION BYウィンドウ(グループ)を定義省略可(全行が1グループ)
ORDER BYウィンドウ内の並び順を定義省略可(ランキング関数では必須)

ROW_NUMBER(): 行番号を付ける

各行に連番を振ります。同じ値でも異なる番号がつきます。

sql
SELECT
    name AS 社員名,
    salary AS 月給,
    ROW_NUMBER() OVER (ORDER BY salary DESC) AS 全社ランキング
FROM employees;

結果:

社員名月給全社ランキング
田中太郎5000001
高橋美咲4800002
小林さくら4700003
山田花子4600004
佐藤花子4500005
.........

PARTITION BY で部署内ランキング

sql
SELECT
    name AS 社員名,
    d.name AS 部署名,
    salary AS 月給,
    ROW_NUMBER() OVER (
        PARTITION BY e.department_id
        ORDER BY salary DESC
    ) AS 部署内ランキング
FROM employees e
INNER JOIN departments d ON e.department_id = d.id;

結果:

社員名部署名月給部署内ランキング
田中太郎開発部5000001
佐藤花子開発部4500002
鈴木一郎開発部3800003
高橋美咲営業部4800001
伊藤健太営業部3500002
渡辺直美営業部3300003
............

PARTITION BY で部署ごとにグループ分けし、各グループ内で ORDER BY salary DESC の順にランキングしています。


RANK() と DENSE_RANK(): ランキング

関数同順位の扱い次の順位
ROW_NUMBER()同値でも異なる番号連番
RANK()同値は同じ番号飛び番(1, 2, 2, 4)
DENSE_RANK()同値は同じ番号連番(1, 2, 2, 3)
sql
SELECT
    name,
    salary,
    ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num,
    RANK() OVER (ORDER BY salary DESC) AS rank,
    DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank
FROM employees;

集計ウィンドウ関数

通常の集計関数(SUM, AVG, COUNT, MAX, MIN)も OVER句と組み合わせられます。

例1: 各行に全社平均を付与

sql
SELECT
    name AS 社員名,
    salary AS 月給,
    AVG(salary) OVER () AS 全社平均,
    salary - AVG(salary) OVER () AS 平均との差
FROM employees;

OVER () と空のカッコにすると、全行が1つのウィンドウになります。

例2: 部署平均との比較

sql
SELECT
    name AS 社員名,
    d.name AS 部署名,
    salary AS 月給,
    ROUND(AVG(salary) OVER (PARTITION BY e.department_id)) AS 部署平均,
    salary - ROUND(AVG(salary) OVER (PARTITION BY e.department_id)) AS 部署平均との差
FROM employees e
INNER JOIN departments d ON e.department_id = d.id;

例3: 累計(Running Total)

sql
SELECT
    name AS 社員名,
    salary AS 月給,
    SUM(salary) OVER (ORDER BY hire_date) AS 給与累計
FROM employees
ORDER BY hire_date;

結果:

社員名月給給与累計
高橋美咲480000480000
田中太郎500000980000
山田花子4600001440000
.........

ORDER BY を指定すると、その順序で累積的に集計されます。


ウィンドウ関数の活用場面

場面使うウィンドウ関数
ランキング表示ROW_NUMBER(), RANK(), DENSE_RANK()
グループ内での相対位置RANK() OVER (PARTITION BY ...)
累計・移動平均SUM() OVER (ORDER BY ...)
前の行との比較LAG(), LEAD()
全体に対する割合SUM(値) / SUM(値) OVER ()

まとめ

ポイント内容
ウィンドウ関数行をグループ化せずに行ごとに集計を行う
OVER句PARTITION BY でグループ、ORDER BY で並び順を指定
ROW_NUMBER連番を振る
RANK / DENSE_RANKランキング(同順位の扱いが異なる)
集計 + OVERSUM, AVG等をウィンドウ関数として使用

チェックリスト

  • ウィンドウ関数とGROUP BYの違いを説明できる
  • OVER (PARTITION BY ... ORDER BY ...) の構文を書ける
  • ROW_NUMBER, RANK, DENSE_RANK の違いを把握した
  • 集計関数をウィンドウ関数として使える

次のステップへ

ウィンドウ関数の基本を学びました。 次のセクションでは、サブクエリとウィンドウ関数を組み合わせた実践演習に挑戦します。


推定読了時間: 30分