SQLの学習、順調に進んでいますか?今回は、データ分析などで非常に強力な武器となる「ウィンドウ関数」について学びましょう。集計関数(`SUM`, `AVG`など)と似ていますが、ウィンドウ関数は集計結果を行にまとめることなく、各行に対して計算結果を返すことができる便利な機能です。
OVER句: ウィンドウの定義
ウィンドウ関数を使う上で最も重要なのが `OVER` 句です。この句を使って、関数がどの範囲の行(ウィンドウ)を対象にするかを指定します。`OVER` 句の中では、主に以下の2つの要素を指定します。
- PARTITION BY <列名>: データを特定の列の値に基づいてグループ(パーティション)に分割します。`GROUP BY` と似ていますが、行を集約しません。省略した場合、テーブル全体が1つのパーティションとみなされます。
- ORDER BY <列名> [ASC|DESC]: 各パーティション内での行の順序を定義します。`RANK` や `ROW_NUMBER` などの順位付け関数や、累計計算などで必要になります。
基本的な構文は以下のようになります。
SELECT
列1,
列2,
ウィンドウ関数名(引数) OVER (
PARTITION BY 列名1 -- どのグループで区切るか (省略可能)
ORDER BY 列名2 [ASC|DESC] -- グループ内でどう並べるか (関数によって必須/任意)
) AS 新しい列名
FROM
テーブル名;
例えば、「部署ごとに」「給与の高い順に」ランキングを付けたい、といった場合に `PARTITION BY 部署名 ORDER BY 給与 DESC` のように指定します。
ROW_NUMBER(): 連番を振る
`ROW_NUMBER()` は、ウィンドウ内の各行に対して、重複のない一意の連番を振る関数です。`ORDER BY` で指定された順序に従って、1から始まる連続した整数を割り当てます。
例: 社員テーブル(employees)の給与(salary)が高い順に連番を振る
SELECT
employee_name,
salary,
ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num
FROM
employees;
この結果、`row_num` 列には給与が最も高い社員から順に1, 2, 3… という連番が表示されます。
RANK(): 順位を付ける(ギャップあり)
`RANK()` は、ウィンドウ内の各行に対して順位を付ける関数です。`ORDER BY` で指定された順序に基づきます。同じ値を持つ行には同じ順位が与えられ、その次の順位は飛ばされます(例: 1位, 2位, 2位, 4位)。
例: 社員テーブル(employees)の給与(salary)が高い順に順位を付ける
SELECT
employee_name,
salary,
RANK() OVER (ORDER BY salary DESC) AS salary_rank
FROM
employees;
もし同額の給与の社員が複数いる場合、彼らには同じ `salary_rank` が付き、その次の順位は欠番になります。
DENSE_RANK(): 順位を付ける(ギャップなし)
`DENSE_RANK()` も `RANK()` と同様に順位を付けますが、同じ値を持つ行に同じ順位を与えた後、次の順位を飛ばしません(例: 1位, 2位, 2位, 3位)。
例: 社員テーブル(employees)の給与(salary)が高い順に、欠番なしで順位を付ける
SELECT
employee_name,
salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_salary_rank
FROM
employees;
`RANK()` との違いは、同順位があった場合でも、次の順位が連続することです。
ROW_NUMBER vs RANK vs DENSE_RANK の比較
3つの順位付け関数の違いを理解するために、簡単な例で比較してみましょう。
Score | ROW_NUMBER() | RANK() | DENSE_RANK() |
---|---|---|---|
100 | 1 | 1 | 1 |
90 | 2 | 2 | 2 |
90 | 3 | 2 | 2 |
80 | 4 | 4 | 3 |
70 | 5 | 5 | 4 |
- `ROW_NUMBER()`: 常にユニークな連番
- `RANK()`: 同順位は同じ番号、次の番号は飛ぶ
- `DENSE_RANK()`: 同順位は同じ番号、次の番号は飛ばない
その他の便利なウィンドウ関数
`ROW_NUMBER`, `RANK`, `DENSE_RANK` 以外にも、様々なウィンドウ関数があります。
- 集計関数 (`SUM`, `AVG`, `COUNT`, `MAX`, `MIN`): `OVER`句と組み合わせることで、パーティションごとの累計や移動平均などを計算できます。
- `LAG()`: パーティション内で、現在の行より前の行の値を取得します。
- `LEAD()`: パーティション内で、現在の行より後の行の値を取得します。
- `FIRST_VALUE()` / `LAST_VALUE()`: パーティション内の最初/最後の値を取得します。
- `NTILE(n)`: パーティションを `n` 個のグループに分割し、各行がどのグループに属するかを示す番号を返します。
これらの関数も `OVER (PARTITION BY … ORDER BY …)` と組み合わせて使います。
まとめ 🎉
ウィンドウ関数は、集計関数のように行をまとめることなく、各行に対して関連する行セット(ウィンドウ)に基づいた計算を行える強力な機能です。
- `OVER`句でウィンドウ(対象範囲と順序)を定義します (`PARTITION BY`, `ORDER BY`)。
- `ROW_NUMBER()` は一意の連番を振ります。
- `RANK()` は同順位を許容し、順位を飛ばします。
- `DENSE_RANK()` は同順位を許容し、順位を飛ばしません。
- 集計関数や `LAG`/`LEAD` などもウィンドウ関数として利用できます。
最初は少し複雑に感じるかもしれませんが、使いこなせるとSQLでのデータ分析の幅が格段に広がります。ぜひ色々なデータで試してみてくださいね!
コメント