该用rank()而非dense_rank()时:需体现并列导致的名次跳空(如1,1,3),适用于赛事颁奖、淘汰制、层级绩效等强调断层与击败人数的场景;而dense_rank()适合分档、前端展示等要求连续顺位(如1,1,2,3)的业务。

什么时候该用 RANK() 而不是 DENSE_RANK()
RANK() 的跳号行为不是缺陷,而是信号——它明确告诉你“有并列,且这个名次被占用了”。比如赛事颁奖、淘汰机制、层级制绩效(A档→B档→C档,中间不能空档但允许多人同档),RANK() 返回的 1, 1, 3 正好体现“两个冠军,没有亚军,直接季军”的业务逻辑。
常见错误现象:WHERE rnk = 2 查不到数据,不是函数错了,是业务上确实没有“唯一第二名”;LIMIT 10 配合 RANK() 取 Top 10 会漏行,因为排名数字不连续,实际返回可能只有 7 行。
- 适用场景:强调断层、需体现“击败了多少人”(如“超过 95% 用户”)、下游系统依赖跳号表达层级
- 必须配
ORDER BY,否则报错Window function 'RANK' requires an ORDER BY clause - 漏写
PARTITION BY不报错,但结果变成全表排名,不是你想要的“每组内独立计算”
为什么 DENSE_RANK() 更适合分档和前端展示
DENSE_RANK() 的 1, 1, 2, 3 是业务最常预期的“顺位感”:两个最高分都是第 1 档,下一个自然就是第 2 档,不跳号。教务系统、BI 看板、Excel 里的 RANK.EQ() 默认就是这种行为,两边结果才能对得上。
常见错误现象:用 RANK() 做“取前三档”,结果漏掉大量本该在第 2 档的记录(因为 rnk = 2 这个值根本没出现);导出后业务方在 Excel 里复核,发现排名对不上,反复沟通成本远高于改一个函数名。
- 适用场景:客户分档(Top 5 档)、绩效等级(S/A/B/C)、需要稳定数量切片的报表
- 同样必须写
OVER (ORDER BY ...),且ORDER BY字段含 NULL 时,不同数据库默认行为不同(PostgreSQLNULLS LAST,MySQL 默认 NULL 排最前) - 别指望它生成唯一编号——只要值相同,就必然并列,无法区分先后
怎么避免排名结果不稳定
当 ORDER BY 字段有重复值(比如多人同分、同薪资),又没加兜底字段,RANK() 和 DENSE_RANK() 都可能在不同执行中返回不同顺序——尤其在 SQL Server 或带 LIMIT 的查询里更明显。
这不是函数问题,是排序本身不确定。例如 ORDER BY score DESC 遇到两个 95 分,数据库可能这次排张三在前,下次李四在前,导致排名波动。
- 安全写法:在
ORDER BY末尾补一个唯一字段,如主键id或时间戳created_at - 正确示例:
ORDER BY score DESC, id ASC - 错误示例:
ORDER BY score DESC(无保序字段) - 注意:所有三个函数(RANK/DENSE_RANK/ROW_NUMBER)都受此影响,不只限于排名函数
别拿 RANK() 当分页或唯一编号用
RANK() 和 DENSE_RANK() 返回的是“名次”,不是“序号”。它们天生并列,无法保证每行唯一。想分页、抽签、导出唯一编号,必须用 ROW_NUMBER()。
常见错误现象:WHERE rnk = 2 在两人并列第 1 时返回空;用 DENSE_RANK() 截取前 10 名,结果返回 12 行(因并列挤进前 3 名次),而业务要的是严格 10 条记录。
- 真正需要唯一递增编号时,写:
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY hire_date DESC, id ASC) -
RANK()和DENSE_RANK()返回类型是BIGINT,定义临时表字段时别用INT,否则超限会截断 - 聚合后想排名?先在子查询或 CTE 里算好聚合值,再对结果集开窗——
OVER里不能直接写AVG(salary)
DENSE_RANK(),如果漏了 PARTITION BY department,全校统排出来的“第 1 档”对班主任就没意义。函数只是工具,谁跟谁比,才决定结果是否可用。











