dense_rank()更适合“取前三”因为其排名连续不跳(如1,2,2,3),能完整包含薪资第1、2、3名的所有员工;而row_number()强行编号、rank()跳名次,均会导致遗漏。需用子查询或cte先计算再过滤。

为什么 DENSE_RANK() 比 RANK() 和 ROW_NUMBER() 更适合“取前三”
因为你要的是“薪资前三的所有员工”,不是“前三个员工”。当多人薪资并列时,ROW_NUMBER() 会强行拆开排名(比如 1,2,2,4),RANK() 会跳名次(1,2,2,4),而 DENSE_RANK() 保证连续且不跳(1,2,2,3)——这样“前三”才真正包含所有薪资排在第 1、2、3 名的员工,不管每档有多少人。
直接写法:用子查询过滤 DENSE_RANK() 结果
不能在 WHERE 中直接用窗口函数,必须先用子查询或 CTE 把排名算出来,再筛 rnk :
SELECT emp_id, name, salary, rnk
FROM (
SELECT emp_id, name, salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk
FROM employees
) ranked
WHERE rnk
<p>注意点:</p>
-
ORDER BY salary DESC是关键,升序会把最低薪排第一 - 如果表里有
NULL薪资,默认会被排在最前面(因DESC),建议加WHERE salary IS NOT NULL预过滤 - MySQL 8.0+、PostgreSQL、SQL Server、Oracle 都支持;SQLite 目前不支持窗口函数
遇到 ties(并列)时的真实结果长什么样
假设数据是:[10000, 9500, 9500, 9000, 9000, 9000, 8500],DENSE_RANK() 输出是:[1,2,2,3,3,3,4]。所以 rnk 会返回前 6 行(即所有薪资 ≥ 9000 的员工),共 6 人——这正是“前三名所有员工”的本意。
容易踩的坑:
- 误用
LIMIT 3:那只会硬截 3 行,漏掉并列的第 3 名员工 - ORDER BY 写成
ASC却没意识到是在找“最低薪前三” - 没处理
NULL,导致一个NULL薪资员工被算作rnk = 1
想按部门分组取“各部门薪资前三”怎么办
只需在 OVER 子句中加 PARTITION BY dept_id:
SELECT dept_id, emp_id, name, salary, rnk
FROM (
SELECT dept_id, emp_id, name, salary,
DENSE_RANK() OVER (
PARTITION BY dept_id
ORDER BY salary DESC
) AS rnk
FROM employees
WHERE salary IS NOT NULL
) ranked
WHERE rnk
<p>这里的关键是:<code>PARTITION BY</code> 必须和业务分组维度完全一致;如果部门字段叫 <code>department_name</code>,就得写那个名字,别手抖写成 <code>dept</code> 导致分组失效。</p>
<p>复杂点在于:不同数据库对 <code>PARTITION BY</code> + <code>ORDER BY</code> 的 NULL 处理可能不一致,生产环境务必用真实数据验证 NULL 是否被归入某一分区顶部。</p>










