row_number() 为相同分数分配不同排名是因为它仅按排序位置严格递增编号,不考虑值重复;rank() 和 dense_rank() 才处理并列,前者跳过后续名次,后者不跳;需在 order by 中添加唯一字段确保结果稳定;若需并列逻辑+唯一序号,须嵌套使用二者。

ROW_NUMBER() 为什么会给相同分数分配不同排名
ROW_NUMBER() 的设计目标就是生成严格递增的唯一序号,它完全不关心值是否重复——哪怕 score 列全是 95,ROW_NUMBER() OVER (ORDER BY score DESC) 也会返回 1, 2, 3, 4… 它只按排序位置“数数”,不是按分数“分组”。所以如果你看到相同分数被排成第1、第2、第3名,这不是 bug,是它本来的功能。
RANK() 和 DENSE_RANK() 才真正处理并列场景
要让相同分数获得相同名次,必须换函数:
-
RANK():并列时占位。比如分数 [100, 95, 95, 90] → 排名 [1, 2, 2, 4](跳过 3) -
DENSE_RANK():并列时不占位。同样数据 → 排名 [1, 2, 2, 3]
选哪个取决于业务规则:奥运奖牌榜用 RANK()(两个银牌,下一个是铜牌,没“第四名”);内部绩效档位常选 DENSE_RANK()(A 档、B 档、C 档,不跳档)。
ORDER BY 子句里加第二排序字段可控制并列顺序
即使用了 RANK(),如果原始数据中多行 score 完全相同,数据库仍可能因无稳定排序而让每次查询结果的“并列者内部顺序”不一致——进而影响谁先拿到第2名、谁后拿。解决方法是在 ORDER BY 里补充唯一字段:
SELECT name, score, RANK() OVER (ORDER BY score DESC, id ASC) AS rank_num FROM students;
这样能保证相同分数下,id 小的永远排前面,结果可复现。漏掉这个,开发环境看着对,上线后可能因数据插入顺序或索引变化导致排名漂移。
需要唯一序号又想保留并列逻辑?只能两层嵌套
没有单个窗口函数能同时满足“相同分数同排名 + 全局唯一序号”。常见做法是先用 DENSE_RANK() 算逻辑名次,再用 ROW_NUMBER() 算物理序号:
SELECT name, score, rank_logic, row_phys
FROM (
SELECT name, score,
DENSE_RANK() OVER (ORDER BY score DESC) AS rank_logic,
ROW_NUMBER() OVER (ORDER BY score DESC, id ASC) AS row_phys
FROM students
) t;
注意:row_phys 不代表“名次”,只是行号;rank_logic 才是你要的并列排名。混淆这两者是线上统计出错的高频原因——尤其当产品说“显示前10名”,却用 row_phys 过滤,会把第10个物理行(可能是第6名)当成第10名返回。










