dense_rank() 解决 rank() 排名间断问题,返回连续排名如 1,1,2,3;rank() 并列占位致跳号(如 1,1,3),dense_rank() 并列不占位、后续紧接,且必须含 order by。

DENSE_RANK() 就是用来解决 RANK() 产生的排名间断问题的,比如你用 RANK() 排名后看到 1, 1, 3, 4 这种跳号,而业务要求必须是 1, 1, 2, 3 ——这时候直接换用 DENSE_RANK(),不用改逻辑、不用加子查询,一行函数就搞定。
为什么 RANK() 会产生间断,而 DENSE_RANK() 不会
RANK() 遇到并列时,会把“被跳过的名次”也计入计数。比如三个人分数为 95, 95, 88,RANK() 给出 1, 1, 3(因为两个第 1 名占了前两名位置,下一位直接从第 3 名开始)。
DENSE_RANK() 只看“有多少个比当前值严格更大的不同值”,所以同样数据返回 1, 1, 2——它不预留空位,只按实际档位递进。
关键区别就这一句:并列不占后续名额。
-
RANK():并列 → 占位 → 后续跳号 -
DENSE_RANK():并列 → 不占位 → 后续紧接 -
ROW_NUMBER():强制唯一编号,根本不存在并列(哪怕值相同也会分出 1/2/3)
DENSE_RANK() 的 PARTITION BY 和 ORDER BY 必须同时存在
DENSE_RANK() 是窗口函数,OVER 子句里 ORDER BY 是强制项,PARTITION BY 是可选但高频使用项。漏掉 ORDER BY 会报错:Incorrect syntax near ')'.
常见误写:
DENSE_RANK() OVER (PARTITION BY dept_id)
正确写法(哪怕只是按主键排,也要显式写):
DENSE_RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC, emp_id)
- 多个排序字段建议加上
emp_id这类唯一列,避免因排序不确定性导致结果波动 -
ORDER BY中不能用整数位置(如ORDER BY 2),SQL Server 不支持 - 如果不需要分区,就只留
ORDER BY,别写空的PARTITION BY ()(语法错误)
和 RANK() 混用时容易忽略的兼容性细节
虽然两者返回类型都是 bigint,但行为差异会影响下游逻辑。比如你原来用 RANK() 写了 WHERE rank 拿 Top 3,换成 <code>DENSE_RANK() 后,可能实际返回超过 3 行(因为并列多,但排名没跳)。
典型陷阱场景:
- 报表中“各科室绩效前 3 名”,用
RANK()可能只返回 2 人(两人并列第 1,下一个是第 3),而DENSE_RANK()会返回 4 人(1,1,2,3) - 分页逻辑依赖排名值做 offset,
DENSE_RANK()的连续性会让页边界更“实”,但也要同步检查ORDER BY是否足够稳定 - 导出 Excel 后人工核对时,发现排名数字变小了(比如原
1,1,4变成1,1,2),不是函数错了,是语义变了
真正要注意的不是“怎么写对”,而是“排名数字变连续之后,业务规则是否还成立”。比如“取排名 ≤ 2 的人发奖金”,DENSE_RANK() 下可能比原来多发——这个得跟产品或财务对齐,而不是 DBA 自行替换函数。










