rank() 生成重复排名是因为其设计逻辑是处理并列:相同值获得相同名次,后续名次跳过;必须配合 over 子句使用,支持分区、多字段排序及 nulls first/last 显式控制,且不改变结果集行数。

为什么 RANK() 会生成重复排名?
RANK() 的设计逻辑就是处理「并列」:当排序字段值相同时,它们获得相同名次,后续名次跳过。比如两个第1名后,下一个是第3名,不是第2名。这和 DENSE_RANK()(不跳号)或 ROW_NUMBER()(强制唯一)有本质区别。
常见误用场景是想“去重后排名”或“连续编号”,结果发现排名中间断号,其实是 RANK() 在按预期工作。
RANK() 必须配合 OVER 子句使用
单独写 RANK() 会报错:ERROR: window function RANK requires a window definition。它不能像普通聚合函数那样直接用在 SELECT 列表里,必须明确指定排序依据和分区范围。
- 最简用法:
RANK() OVER (ORDER BY score DESC)—— 全表按 score 降序排,生成全局名次 - 分组内排名:
RANK() OVER (PARTITION BY subject ORDER BY score DESC)—— 每个学科独立排名 - ORDER BY 中可含多个字段,如
ORDER BY grade DESC, name ASC,解决并列时的次级排序
ORDER BY NULLS FIRST/LAST 影响并列判断
NULL 值默认排在末尾(PostgreSQL/Oracle),但若字段含 NULL,且你没显式声明 NULLS FIRST 或 NULLS LAST,不同数据库行为可能不一致,导致同一 SQL 在 MySQL(不支持该语法)和 PostgreSQL 上排名结果不同。
稳妥做法是显式控制:
RANK() OVER (ORDER BY score DESC NULLS LAST)
这样能确保所有 NULL 成绩被统一归到最低名次段,避免因隐式排序导致部分 NULL 被“挤”进中间名次。
性能注意:RANK() 是窗口函数,不触发 GROUP BY
很多人误以为加了 RANK() 就得配 GROUP BY,其实不然。窗口函数在逻辑上先完成排序和编号,再返回原行数——结果集行数不变,每行多一个排名列。
这意味着:
- 可以和普通字段、聚合函数混用,但聚合函数需搭配
GROUP BY,而RANK()不需要 - 大数据量时,
OVER (ORDER BY ...)会触发全量排序,若没在score字段建索引,可能明显拖慢查询 - MySQL 8.0+、PostgreSQL、SQL Server 支持;SQLite 3.25+ 支持;老版本 MySQL(FUNCTION xxx.RANK does not exist
并列排名本身逻辑清晰,真正容易卡住的是环境兼容性和 NULL 处理边界——别光盯着函数怎么写,先确认你的数据库版本和数据里有没有空值。











