RANK()和DENSE_RANK()均需配合OVER(ORDER BY)使用,区别在于并列时RANK()跳号(如1,1,3)、DENSE_RANK()不跳号(如1,1,2),且二者仅可用于SELECT列表,不可出现在WHERE或GROUP BY中。

MySQL 8.0中RANK()和DENSE_RANK()的基本用法区别
MySQL 8.0开始原生支持窗口函数,RANK() 和 DENSE_RANK() 都能实现分组内排序编号,但处理并列的方式不同:遇到相同值时,RANK() 会跳过后续名次(比如 1,1,3),而 DENSE_RANK() 不跳过(1,1,2)。这个差异直接影响业务语义——比如“年级前10名”用 RANK() 可能实际返回12人,“同分同名次且不空号”则必须选 DENSE_RANK()。
两者语法完全一致,都依赖 OVER() 子句定义排序逻辑:
SELECT name, score,
RANK() OVER (ORDER BY score DESC) AS rank_num,
DENSE_RANK() OVER (ORDER BY score DESC) AS dense_num
FROM students;
按部门分组排名时必须加PARTITION BY
如果要查“每个部门内员工薪资排名”,漏写 PARTITION BY dept 就会变成全表统一排名,这是新手最常踩的坑。窗口函数不会自动按 GROUP BY 分组,它只认 OVER() 里的显式声明。
正确写法示例:
SELECT dept, name, salary,
RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS dept_rank
FROM employees;
-
PARTITION BY dept表示按部门切片,各组独立计算排名 - ORDER BY 必须存在,否则报错
Window 'w' lacks an ORDER BY clause - 若需先按部门、再按入职时间保序,可写
ORDER BY salary DESC, hire_date ASC
ORDER BY 中NULL值影响排名结果
MySQL 8.0默认把 NULL 当作最小值(等价于 NULLS FIRST),所以 ORDER BY score DESC 会让 NULL 排在最后;但若字段本身含大量 NULL,且你希望它们排最前或忽略,就得显式控制:
- 让 NULL 排最前:
ORDER BY score DESC NULLS FIRST - 让 NULL 排最后(默认行为):
ORDER BY score DESC NULLS LAST - 彻底排除 NULL 行:
WHERE score IS NOT NULL(推荐在 WHERE 阶段过滤,比在窗口里处理更高效)
不声明 NULLS FIRST/LAST 时,不同 MySQL 版本或 SQL 模式下行为可能不一致,线上环境务必显式指定。
性能注意:大表上慎用无索引的ORDER BY字段
窗口函数的 ORDER BY 字段如果没有索引,MySQL 会强制触发临时表 + filesort,数据量稍大(比如百万级)就明显变慢。这不是语法问题,而是执行计划层面的瓶颈。
- 检查执行计划:
EXPLAIN SELECT ... RANK() OVER (ORDER BY unindexed_col) ...,关注Extra列是否出现Using temporary; Using filesort - 给排序字段建索引:
ALTER TABLE students ADD INDEX idx_score_desc (score DESC);(MySQL 8.0 支持降序索引) - 避免在
ORDER BY中用函数或表达式,如ORDER BY ABS(score)会导致索引失效
真正容易被忽略的是:即使加了索引,如果 PARTITION BY 和 ORDER BY 字段组合无法命中联合索引前缀,照样会回退到临时表。这时候得按分区键+排序键建联合索引。


















