部署ごとに給与額の上位3種類に入る社員を、同額の社員も全員含めて取得するには、DENSE_RANK()を使います。
WITH RankedEmployees AS (
SELECT
e.id,
e.name,
e.department_id,
e.salary,
DENSE_RANK() OVER (
PARTITION BY e.department_id
ORDER BY e.salary DESC
) AS salary_rank
FROM employees e
)
SELECT
id,
name,
department_id,
salary
FROM RankedEmployees
WHERE salary_rank <= 3
ORDER BY department_id, salary DESC;
DENSE_RANK()は同じ給与に同じ順位を付け、順位の飛び番を作りません。例えば給与が 50000, 50000, 45000, 40000 の場合、順位は 1, 1, 2, 3 になります。そのため、salary_rank <= 3で給与額の上位3種類に該当する社員を、同額の人も含めて取得できます。
RANK()では同じ給与の人数に応じて順位が飛ぶため、給与額の上位3種類を取得する要件には通常DENSE_RANK()が適しています。
このクエリは、MySQL 8.0以降、PostgreSQL、SQL Server、Oracle、SQLite 3.25.0以降など、ウィンドウ関数に対応するデータベースで利用できます。MySQL 5.7以前などでは、ウィンドウ関数を使わない相関サブクエリなどの代替が必要です。
なお、salaryにNULLが含まれる場合、ORDER BY salary DESCでのNULLの位置はデータベースによって異なります。NULLを上位3種類の対象外にする場合は、例えば次のように明示できます。
WHERE salary IS NOT NULL
AND salary_rank <= 3
また、department_idがNULLの行は、通常は同じパーティションとしてまとめて順位付けされます。