DENSE_RANK()能解决“并列不跳号”问题,因其语义为并列只占一个名次、后续名次紧接,满足并列、连续、不跳空三条件;使用时须搭配OVER(ORDER BY...),且不可在WHERE中引用其别名。

为什么DENSE_RANK()能解决“并列不跳号”问题
DENSE_RANK() 的语义就是「并列只占一个名次,后续名次紧接」。它不像 RANK() 那样在并列后跳过被占用的名次(比如 1,1,3),也不像 ROW_NUMBER() 那样强制唯一编号(1,2,3,4)。当你需要真实反映“第几名”的业务逻辑——比如年级总分榜、销售业绩段位、考试等级划分——DENSE_RANK() 是唯一能同时满足「并列」「连续」「不跳空」三个条件的窗口函数。
怎么写才不会报错或结果错乱
常见错误不是函数用错,而是执行时机和语法结构没对齐。必须注意:
-
DENSE_RANK()必须跟OVER (ORDER BY ...)一起出现,单独写会报语法错误 - 不能在
WHERE子句里直接引用DENSE_RANK()的别名(如WHERE drk ),因为窗口计算发生在 WHERE 过滤之后;得用子查询或 CTE 包一层 - 如果要按部门分别排名,必须加
PARTITION BY dept_id,漏掉就变成全表统一排名 - ORDER BY 字段若存在重复值又没加稳定排序字段(如
id ASC),可能导致同分数据每次执行排名顺序不一致
MySQL 8.0+ 和 PostgreSQL 中的实际写法差异
主流数据库都支持 DENSE_RANK(),但 NULL 处理方式不同,容易导致排名意外前置:
- PostgreSQL / Oracle / SQL Server 支持
ORDER BY score DESC NULLS LAST,可把空值排到最后 - MySQL 8.0 不支持
NULLS LAST,得用ORDER BY COALESCE(score, -999999) DESC模拟 - SQLite(截至 3.45)仍不支持
DENSE_RANK(),必须用自连接或相关子查询硬实现,性能差且易出错
示例(MySQL 安全写法):
SELECT name, score, DENSE_RANK() OVER (ORDER BY COALESCE(score, -1) DESC) AS drk FROM students;
什么时候不该用DENSE_RANK()
它不是万能解药。以下场景反而会引入问题:
- BI 工具(如 Power BI、Tableau)做动态筛选时,对含
DENSE_RANK()的字段下钻常不稳定,前端可能无法正确识别“第2名”对应哪些行 - 要做“严格 Top N”(比如只取前10条记录,不多不少),用
DENSE_RANK()可能返回 12 条(因为第10名有3人并列),这时应改用ROW_NUMBER() - 配合
GROUP BY使用:窗口函数运行在聚合前,一旦用了GROUP BY,原始行已丢失,DENSE_RANK()就无从计算
真正容易被忽略的是:排名是否需要跨组连续。比如按城市分组后,每个城市的“第1名”都叫1,这没问题;但如果你希望所有城市的第1名统称“第1梯队”,那就不能用 PARTITION BY,而要先聚合再全局排名。

















