结论:需名次断层时必须用rank();如分数[100,95,95,90],rank()返回[1,2,2,4],因并列占位导致第3名跳过,确保段位感与强区分。

直接说结论:要保留并列名次且后续名次连续(比如两个第2名后是第3名),用 DENSE_RANK();要保留并列但后续名次跳过空档(比如两个第2名后直接是第4名),用 RANK()。选错函数,Top N 查询就漏数据,分档逻辑就断层。
什么时候必须用 RANK()?
当业务明确要求“段位感”或“名次断层”时,RANK() 是唯一选择。典型场景包括赛事榜单、奖学金评定、绩效等级强区分等——并列者共享名次,但下一个有效名次必须跳过已占位数。
- 例如分数为
[100, 95, 95, 90],RANK() OVER (ORDER BY score DESC)返回[1, 2, 2, 4],因为两个 95 分占了第 2 名,第 3 名被跳过 - 做“前 3 名”查询时,
WHERE rk (需子查询包裹)会返回 3 行:100 分、两个 95 分;而如果误用 <code>ROW_NUMBER(),只会取到 100 和第一个 95,漏掉第二个 -
RANK()对 ORDER BY 的重复值完全敏感,哪怕只差毫秒的时间戳,只要表达式结果相同,就一定并列——这点比手写变量逻辑更可靠
为什么 DENSE_RANK() 更适合梯队划分?
DENSE_RANK() 不跳号,本质是把排序结果“压缩成连续整数档位”,天然适配 ABC 分档、销售等级、内部评级等不允许空档的场景。
- 同样数据
[100, 95, 95, 90],DENSE_RANK() OVER (ORDER BY score DESC)返回[1, 2, 2, 3],第 3 档始终存在 - 若业务规则是“销售额前 3 档对应 A/B/C 级客户”,用
RANK()可能导致某档彻底消失(比如没有第 3 名),而DENSE_RANK()保证档位编号连续可映射 - 注意:
DENSE_RANK()不合并行、不改变行数,只是编号不跳——有人误以为它会“去重”,其实不会
ORDER BY 写错会导致并列逻辑失效
多条件排序时,只有 ORDER BY 中真正决定“是否相等”的字段才参与并列判定;辅助破 ties 的字段必须放在末尾,且不能混入主排序逻辑。
- 错误写法:
RANK() OVER (ORDER BY sales DESC, created_at ASC)—— 只要created_at不同,哪怕sales相同,也会被当成不同名次 - 正确做法:
RANK() OVER (ORDER BY sales DESC) AS rk,再在外层ORDER BY sales DESC, created_at ASC控制展示顺序 - 如果排序字段含
NULL,MySQL 默认NULLS LAST,PostgreSQL 默认NULLS FIRST,会影响排名位置;显式加NULLS LAST(PostgreSQL/Oracle/SQL Server 支持)或用COALESCE(score, 0)(MySQL)统一处理
WHERE 里直接写 RANK() 就会报错
窗口函数在 SQL 执行顺序中晚于 WHERE,所以别名 rk 在 WHERE 阶段根本不存在——这是最常踩的坑,不是语法错,是执行时序错。
- 错误:
SELECT *, RANK() OVER (ORDER BY score DESC) AS rk FROM students WHERE rk → 报错 <code>column "rk" does not exist - 必须用子查询或 CTE:
SELECT * FROM (SELECT *, RANK() OVER (ORDER BY score DESC) AS rk FROM students) t WHERE t.rk - MySQL 5.7 及更早版本不支持窗口函数,强行用会报
ERROR 1064 (42000);升级到 8.0+ 是硬前提
真正容易被忽略的是:并列判断只看 ORDER BY 表达式的最终计算值是否相等,和字段本身是否唯一无关;哪怕你写了 ORDER BY score DESC, id ASC,只要两行 score 相同,它们就并列——id 只影响输出顺序,不参与名次判定。










