ROW_NUMBER() 是窗口函数,必须配合 PARTITION BY 和 ORDER BY 使用;漏掉 PARTITION BY 会导致全表排名而非分组排名;NULL 值在 PARTITION BY 中被归为同一组,需用 WHERE department IS NOT NULL 过滤;不能在 WHERE 中直接引用 ROW_NUMBER() 别名。

ROW_NUMBER() 必须配合 PARTITION BY 和 ORDER BY 使用
单独写 ROW_NUMBER() 会报错,它不是普通函数,而是窗口函数,必须明确“按什么分组”和“在组内怎么排序”。查各部门薪资前三名,分组字段是部门(比如 department),排序依据是薪资(比如 salary 降序)。
常见错误是漏掉 PARTITION BY,结果得到的是全表连续排名,而不是每个部门独立排前三。
- 正确写法:
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) - 错误写法:
ROW_NUMBER() OVER (ORDER BY salary DESC)(全表排名) - 如果部门字段含空值,
PARTITION BY会把所有NULL归为同一组,需提前WHERE department IS NOT NULL
WHERE 不能直接过滤窗口函数结果
ROW_NUMBER() 是在 SELECT 阶段计算的,而 WHERE 在逻辑执行顺序中早于 SELECT,所以不能在同一个查询里写 WHERE rn —— 会提示“列不存在”或“无效标识符”。
必须用子查询或 CTE 把带排名的结果先算出来,再外层筛选。
- 推荐 CTE 写法(清晰、易读):
WITH ranked AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn FROM employees ) SELECT * FROM ranked WHERE rn
- 子查询也可行,但嵌套深时可读性下降
- 注意:如果某部门人数不足 3 人,
rn 仍会返回全部,这是预期行为
ROW_NUMBER() 和 RANK() / DENSE_RANK() 的区别影响“前三名”语义
当一个部门内有相同薪资时,三种函数行为不同:
-
ROW_NUMBER()强制不重复编号(1,2,3,4…),哪怕薪资相同;可能把并列第二的人挤出前三 -
RANK()并列则同名,跳过后续名次(1,2,2,4),此时“前三名”可能返回 4 条记录 -
DENSE_RANK()并列同名,不跳号(1,2,2,3),更贴近日常“前三”的理解
如果你的需求是“薪资最高的三档人”,且允许并列,DENSE_RANK() 更稳妥;如果明确要“最多三人”,才用 ROW_NUMBER()。
性能注意:大表上慎用未加索引的 ORDER BY 字段
ROW_NUMBER() 的 ORDER BY 部分若落在无索引字段(如 salary),窗口计算会触发全局排序,数据量超百万时可能明显变慢。
- 建议在
(department, salary)上建联合索引,尤其当department基数不高时效果显著 - MySQL 8.0+、PostgreSQL、SQL Server 都支持该优化;旧版 MySQL 不支持窗口函数,需用变量模拟,逻辑更脆弱
- 测试时可用
EXPLAIN看是否走了索引或出现Using filesort
实际业务中,“前三名”常隐含去重、排除试用期员工、按最新薪资快照计算等条件,这些得在最内层 FROM 或 WHERE 就处理干净,别堆到窗口函数之后。










