dense_rank()是mysql 8.0+支持的窗口函数,生成连续并列排名(相同值同名次、不跳号),需配合over(partition by…order by…)使用,不支持mysql 5.7及以下版本。

MySQL 8.0 中 DENSE_RANK() 的基本用法
DENSE_RANK() 是窗口函数,MySQL 8.0+ 原生支持,用于生成连续的并列排名(相同值不跳号)。它和 RANK() 的区别在于:当有并列时,RANK() 会跳过后续名次,而 DENSE_RANK() 不跳——比如两个第1名后,下一个是第2名,不是第3名。
必须搭配 OVER() 子句使用,且需指定 ORDER BY;PARTITION BY 可选,用于分组内独立排序。
常见错误:在 MySQL 5.7 或更低版本执行会报错 FUNCTION xxx.DENSE_RANK does not exist —— 确认版本用 SELECT VERSION();,低于 8.0 就不能用。
按成绩分组并列排名:带 PARTITION BY 的典型场景
例如统计每个班级内学生成绩的密集排名,相同分数排同一名,且名次连续:
SELECT class, name, score, DENSE_RANK() OVER (PARTITION BY class ORDER BY score DESC) AS rank_in_class FROM students;
关键点:
-
PARTITION BY class表示“每班单独计算”,不加则全表统一排名 -
ORDER BY score DESC决定升序/降序;成绩高者排前面,所以用DESC - 若
score为NULL,默认排在最前(DESC下);如需把NULL当最低处理,加NULLS LAST(MySQL 8.0.22+ 支持)
DENSE_RANK() 和 RANK()、ROW_NUMBER() 的行为差异
三者都依赖相同 OVER() 结构,但逻辑不同:
-
ROW_NUMBER():严格按顺序编号,哪怕值相同也给不同号(1,2,3…) -
RANK():并列时给相同号,但跳过后续名次(1,1,3,4…) -
DENSE_RANK():并列时给相同号,后续名次不跳(1,1,2,3…)
示例数据 [95, 95, 87, 87, 87] 排名结果对比:
score | ROW_NUMBER | RANK | DENSE_RANK 95 | 1 | 1 | 1 95 | 2 | 1 | 1 87 | 3 | 3 | 2 87 | 4 | 3 | 2 87 | 5 | 3 | 2
注意:MySQL 对 NULL 的排序行为默认与 PostgreSQL 不同,且旧版(NULLS FIRST/LAST,容易导致排名顺序不符合预期。
性能与索引建议:避免全表扫描
DENSE_RANK() 是窗口函数,执行时需对 ORDER BY 字段做排序。如果数据量大、没索引,会导致慢查询。
优化方式:
- 确保
ORDER BY字段(如score)上有索引;复合排序时建联合索引,如(class, score)能加速PARTITION BY class ORDER BY score DESC - 避免在
WHERE条件中过滤后仍对全表开窗——先WHERE再OVER,否则窗口仍基于原始结果集计算 - 慎用
SELECT *+ 窗口函数,尤其在大宽表上;只查必要字段可减少排序内存开销
实际线上遇到排名查询超时,第一反应不是换函数,而是看执行计划里 Using filesort 是否出现在窗口操作前,以及对应字段有没有支撑索引。











