dense_rank()比rank()更适合并列不跳号场景,因其对相同值赋予相同排名且后续名次紧接(如1,1,1,2),而rank()会跳过被占用名次(如1,1,1,4);典型用于成绩榜、业绩榜等需档位连续的业务。

为什么DENSE_RANK()比RANK()更适合并列不跳号场景
当多个记录值相同时,DENSE_RANK()会赋予它们相同排名,且后续排名紧接上一个,不跳号;而RANK()虽然也并列,但会跳过被占用的名次。比如三个人并列第1,DENSE_RANK()给出 1,1,1,2;RANK()给出 1,1,1,4。
典型适用场景:学生成绩榜单、销售员业绩榜、按城市人口排序后取前5——要求“第5名”必须是真实第5个不同档位,不能因为并列就直接跳到第8名。
- 必须配合
ORDER BY使用,否则报错:Window function DENSE_RANK requires an ORDER BY clause -
PARTITION BY可选,用于分组内独立排名(如按班级分别排成绩) - 不支持
ROWS BETWEEN等帧子句——DENSE_RANK()是逻辑位置函数,只依赖排序,不依赖物理行偏移
基础写法和常见错误写法对比
正确写法必须把 DENSE_RANK() 放在 SELECT 列表中,并明确 OVER (ORDER BY ...):
SELECT name, score, DENSE_RANK() OVER (ORDER BY score DESC) AS rank_no FROM students;
以下写法都会报错或结果异常:
- 漏掉
OVER:直接写DENSE_RANK() ORDER BY score DESC→ 语法错误 - 在
WHERE或GROUP BY中使用DENSE_RANK()→ 窗口函数不能出现在这些子句中 - 用
ORDER BY DENSE_RANK() OVER (...)做最终排序 → 虽然语法合法,但可能掩盖原始数据顺序,建议先算排名再单独ORDER BY rank_no, score DESC
和ROW_NUMBER()、RANK()混用时要注意什么
三者行为差异直接影响业务语义:
-
ROW_NUMBER():严格按顺序编号,相同值也会分出 1,2,3 —— 适合需要唯一序号的场景(如分页取第N条) -
RANK():并列时同号,但跳号 —— 适合强调“击败了多少人”,比如“他和两人并列第2,实际超过了5人” -
DENSE_RANK():并列时同号,不跳号 —— 适合强调“处于第几梯队”,比如“所有95分以上都算第一梯队”
如果误把 ROW_NUMBER() 当成 DENSE_RANK() 用,在统计“进入前10名的人数”时会多算(因并列者被拆成不同序号),导致名额溢出。
MySQL 8.0+ 和 PostgreSQL 的兼容性细节
两者都完全支持标准语法,但有两处容易忽略:
- MySQL 5.7 及更早版本不支持窗口函数,强行执行会报错:
FUNCTION your_db.DENSE_RANK does not exist—— 必须确认版本 - PostgreSQL 中若
ORDER BY字段含NULL,默认NULLS LAST;MySQL 8.0 默认NULLS FIRST。若业务要求NULL排末尾,需显式写ORDER BY score DESC NULLS LAST - SQL Server 同样支持,但旧版(2005–2012)只支持
RANK()和ROW_NUMBER(),DENSE_RANK()是从 SQL Server 2012 开始引入
真正麻烦的不是语法,而是团队里有人在旧环境调试时没意识到函数不可用,或者没注意 NULL 排序方向导致线上榜单错位。










