dense_rank()能解决并列不跳号问题,因其为相同值分配相同排名且后续排名紧接递增(如两个第2名后是第3名);它必须配合over()使用,按排序字段降序常用于成绩、销售等需直观“档位”的场景。

为什么 DENSE_RANK() 能解决并列不跳号
当多个行在排序字段上值相同时,DENSE_RANK() 给它们分配相同排名,且后续排名紧接其后——比如两个第2名之后是第3名,而不是第4名。这和 RANK()(会跳号)以及 ROW_NUMBER()(强制唯一编号)有本质区别。
核心在于“稠密”:它不预留被跳过的数字位置。
- 适用场景:成绩榜单、销售排行榜、按部门绩效分档等需要直观反映“第几档”的场合
- 注意:
DENSE_RANK()是窗口函数,必须配合OVER()子句使用,不能单独出现在SELECT列表中而不带窗口定义 - 排序方向直接影响结果:默认升序(ASC),数值小的排前面;业务中常需
ORDER BY score DESC让高分得第1名
DENSE_RANK() 基本写法与常见错误
正确结构是:SELECT ..., DENSE_RANK() OVER (ORDER BY column_name [DESC]) AS rank_col。漏掉 OVER 或括号不匹配会直接报错,比如 ERROR: window function requires an OVER clause。
- 错误示例:
DENSE_RANK() ORDER BY score DESC—— 缺少OVER关键字和括号 - 错误示例:
DENSE_RANK() OVER ORDER BY score DESC——OVER后少了括号 - 多字段排序没问题:
OVER (ORDER BY department ASC, salary DESC),先按部门升序,同部门内高薪优先 - 别名必须用
AS显式声明,否则某些数据库(如 PostgreSQL)会把列名默认为dense_rank,容易和函数名冲突
和 RANK()、ROW_NUMBER() 的实际差异对比
三者都生成序号,但逻辑不同。假设数据按分数降序排列:[95, 95, 87, 87, 87, 76]:
-
ROW_NUMBER()→ [1, 2, 3, 4, 5, 6](纯顺序编号,无视重复) -
RANK()→ [1, 1, 3, 3, 3, 6](并列后跳过已占位数:两个第1名,所以下一个是第3名) -
DENSE_RANK()→ [1, 1, 2, 2, 2, 3](并列后不跳,下一名就是当前最大排名+1)
如果你看到排名序列里出现“1,1,3,3,4”,那基本是误用了 RANK();而“1,1,2,2,3”才是 DENSE_RANK() 的典型输出。
MySQL 8.0+ 和旧版兼容性问题
DENSE_RANK() 在 MySQL 8.0+、PostgreSQL 8.4+、SQL Server 2005+、Oracle 8i+ 都原生支持。但如果你还在用 MySQL 5.7 或更早版本,它不可用,强行写会报错 FUNCTION xxx.DENSE_RANK does not exist。
- 替代方案只能靠变量模拟,但复杂且易出错(尤其涉及多排序字段或大数据量时)
- 确认版本:运行
SELECT VERSION(); - 部分云数据库(如阿里云 RDS MySQL 5.7)即使标称兼容高级特性,也可能未开启窗口函数支持,需检查参数
optimizer_switch中是否启用window_functions
真正要落地,第一件事不是写语句,而是查清你连的是哪个版本、哪个引擎——不然写得再对也执行不了。










