rank()将相同得分排成相同名次并跳过后续名次,因其设计逻辑是“并列即同名,名次不连续”,如得分95、95、87对应名次1、1、3。

为什么RANK()会把相同得分排成相同名次但跳过后续名次
RANK() 的设计逻辑就是「并列即同名,名次不连续」。比如三个人得分分别是 95、95、87,RANK() 返回的是 1、1、3 —— 它不是漏了第 2 名,而是明确表示「前两人并列第 1,下一位自然就是第 3」。
这在竞赛场景里很合理:并列冠军之后,铜牌确实是第三名,不是第二名。
- 它只看
ORDER BY字段的值是否相等,相等就给相同秩,然后跳过被占用的后续数字 - 不依赖物理行序,只依赖排序后分组的逻辑位置
- 如果没写
ORDER BY,RANK()报错:window function requires an OVER clause with ORDER BY
RANK() 和 DENSE_RANK()、ROW_NUMBER() 的关键区别
三者都需 OVER (ORDER BY score DESC),但处理并列的方式完全不同:
-
RANK():并列 → 同名次 → 跳过后续名次(1,1,3,4) -
DENSE_RANK():并列 → 同名次 → 不跳过(1,1,2,3) -
ROW_NUMBER():绝不并列,按查询顺序强行编号(1,2,3,4,哪怕 score 完全一样)
竞赛排名通常用 RANK() 或 DENSE_RANK();若规则写明「同分不同名」才考虑 ROW_NUMBER(),但这时必须加二级排序(如按提交时间),否则结果不可重现。
实际写法:必须带 PARTITION BY 吗?
不需要。竞赛全局排名直接用:
SELECT name, score, RANK() OVER (ORDER BY score DESC) AS rank_num FROM contestants;
但如果要按组排名(比如「每所高校内部排名」),就得加 PARTITION BY school:
SELECT name, school, score,
RANK() OVER (PARTITION BY school ORDER BY score DESC) AS rank_in_school
FROM contestants;
-
PARTITION BY是独立维度,不影响RANK()在每个分区内的计算逻辑 - 分区字段不能出现在
ORDER BY里(语法错误),但可以和排序字段共存于 SELECT 中 - MySQL 8.0+、PostgreSQL、SQL Server、Oracle 都支持;SQLite 直到 3.25+ 才支持窗口函数
容易被忽略的 NULL 处理细节
默认情况下,NULL 在 ORDER BY score DESC 中排最前还是最后,取决于数据库方言:
- PostgreSQL / SQL Server:
NULLS LAST(默认),即NULL排末尾,不影响正常排名连续性 - MySQL:
NULL视为最小值,DESC下排最前,可能导致RANK()给NULL返回1
稳妥做法是显式控制:
RANK() OVER (ORDER BY score DESC NULLS LAST)
或者提前过滤/转换:WHERE score IS NOT NULL,避免把缺考者混进有效排名里。
真正麻烦的是多人同为 NULL —— 它们会被一起排成同一个 RANK() 值(比如全是 1),而这不是 bug,是标准行为。











