并列不跳号的排名
同一组数据按分值排名时,并列怎么算有三种口径:并列名次相同且后续不跳号、并列名次相同但后续跳号、以及完全不允许并列。三种结果的差别只在并列处体现,但展示效果与业务含义完全不同——榜单上「并列第三名之后是第四名」还是「直接跳到第五名」,是产品需要明确的问题,不是开发随手选一个函数的事。
基本信息
| 适用平台 | MySQL 8.0 / PostgreSQL 12 及以上 |
|---|---|
| 不跳号排名 | 并列同名次,后续编号连续 |
| 跳号排名 | 并列同名次,后续按占用位次跳号 |
| 行号 | 严格按顺序编号,不产生并列 |
| 分区能力 | 三者都支持按分区分别排名 |
典型使用场景
- 考试或考核结果:同分并列,后续名次如何展示
- 销售榜单:同额并列时的名次展示规则
- 会员等级分档:按累计消费额确定档位,并列归入同一档
- 分组内排名:每个部门内部单独排名
- 对比三种排名函数在同一次查询里的差异
三种排名并列写在一起
三种排名并列写在一起
-- 三种排名同时输出,便于核对并列处差异
SELECT name, score,
ROW_NUMBER() OVER (ORDER BY score DESC) AS rn, -- 严格编号,不并列
RANK() OVER (ORDER BY score DESC) AS rk, -- 并列同名次,后续跳号
DENSE_RANK() OVER (ORDER BY score DESC) AS drnk -- 并列同名次,后续不跳号
FROM exams
ORDER BY score DESC, name;
-- 分组内排名:每个班级内部各自排名
SELECT class_id, name, score,
DENSE_RANK() OVER (PARTITION BY class_id ORDER BY score DESC) AS drnk
FROM exams
ORDER BY class_id, drnk;
-- 取每组前若干名:先排名再筛行号
SELECT * FROM (
SELECT class_id, name, score,
ROW_NUMBER() OVER (PARTITION BY class_id ORDER BY score DESC) AS rn
FROM exams
) t WHERE rn <= 3;并列处的结果差异
| 分值序列 | 行号排名 | 跳号排名 | 不跳号排名 |
|---|---|---|---|
| 100 | 1 | 1 | 1 |
| 95 | 2 | 2 | 2 |
| 95 | 3 | 2 | 2 |
| 90 | 4 | 4 | 3 |
| 85 | 5 | 5 | 4 |
分值与排序规则要一起看:如果分值列是字符串,排序会按字典序而不是数值大小,出现「100 排在 99 前面」的情况,名次自然也是错的。
取「每组前几名」时要用行号而不是不跳号排名:不跳号排名在并列时会一次取出多于预期的行数,例如并列第二的两行都会入选。