rank()返回重复且跳号的排名,因相同值获同名次、后续名次跳过;组内排名需用partition by;不能在where/having中直接过滤,须用子查询或cte。

为什么RANK()返回的排名有重复且跳号?
RANK() 是窗口函数,它按指定排序规则给行打序号,遇到相同值时赋予相同排名,后续排名自动跳过。比如三行并列第2名,下一行就是第5名,不是第3名——这是它和 ROW_NUMBER()、DENSE_RANK() 的核心区别。
常见错误是误以为 RANK() 会连续编号,结果在分页或阈值筛选(如“取前10名”)时漏掉数据。例如:
SELECT name, score, RANK() OVER (ORDER BY score DESC) AS rk FROM students;
若 score = 95 出现两次,则两行 rk 都是 1,下一个最高分 92 对应的 rk 就是 3。
- 需要连续不跳号 → 改用
DENSE_RANK() - 严格按物理顺序编号 → 用
ROW_NUMBER() - 只关心“并列第几”,且接受跳号 →
RANK()正确
如何在分组内用RANK()做组内排名?
必须配合 PARTITION BY 子句,否则 RANK() 会在整张表上计算,不是按组独立排名。
典型场景:每个班级里按成绩排“班内名次”。错误写法是先 GROUP BY 再套 RANK(),那会报错或逻辑错;正确写法是在窗口定义里加 PARTITION BY class_id:
SELECT class_id, name, score,
RANK() OVER (PARTITION BY class_id ORDER BY score DESC) AS class_rank
FROM students;
注意:PARTITION BY 的字段必须出现在 SELECT 列表中(或至少被查询上下文支持),否则某些数据库(如 MySQL 8.0+)会报错;PostgreSQL 更宽松,但语义仍需对齐。
- 分区字段类型要一致,比如
class_id混用字符串和数字会导致分区断裂 - 如果分区键含 NULL,默认各 NULL 自成一组(行为因数据库而异,MySQL 和 PostgreSQL 均如此)
- ORDER BY 必须存在,否则语法错误
RANK()能直接用于HAVING或WHERE过滤排名结果吗?
不能。窗口函数不能出现在 WHERE 或 HAVING 中,因为它们在 WHERE 之后、GROUP BY 之后(如有)、ORDER BY 之前执行。想筛出“班内前3名”,得用子查询或 CTE:
WITH ranked AS (
SELECT class_id, name, score,
RANK() OVER (PARTITION BY class_id ORDER BY score DESC) AS rk
FROM students
)
SELECT * FROM ranked WHERE rk <p>另一个常见坑:在聚合查询中混用窗口函数,比如 <code>SELECT AVG(score), RANK() OVER (...) FROM t GROUP BY x</code>,这会报错——窗口函数和普通聚合不能共存于同一层 SELECT,除非窗口函数作用在聚合结果上(即先 GROUP BY,再套一层窗口)。</p>
- 别在 WHERE 里写
RANK() > 1,一定报错 - CTE 或子查询是安全且可读性最好的解法
- 部分数据库(如 BigQuery)支持 QUALIFY 子句,可直接
QUALIFY RANK() OVER (...) ,但非 SQL 标准,移植性差
ORDER BY在RANK()里没写DESC会怎样?
默认升序(ASC),也就是最小值排第1名。业务中多数排名需求是“高分优先”,漏写 DESC 会导致结果完全相反,而且不容易一眼发现——尤其当数据分布偏斜时(比如大部分分数集中在60–70分),看起来“排名”似乎也挺合理。
更隐蔽的问题是:如果排序字段含 NULL,默认情况下 NULL 被视为最小值(在 ASC 下排最前,在 DESC 下排最后),这会影响 RANK() 的起始位置。例如:
RANK() OVER (ORDER BY score DESC) —— NULL 排最后,不影响高分段排名
RANK() OVER (ORDER BY score) —— NULL 排第一,<code>rk = 1</code> 可能全是 NULL 行
- 显式写
ORDER BY score DESC NULLS LAST(PostgreSQL/Oracle 支持)可控制 NULL 位置 - MySQL 不支持
NULLS FIRST/LAST,NULL 总是排在 ASC 结果开头、DESC 结果末尾 - 只要涉及 NULL,务必用小样本验证排名输出
实际用 RANK() 时,最常卡住的地方不是语法,而是没想清楚“并列是否该跳号”以及“分区边界是否真对齐业务分组”。一旦分区字段有隐式类型转换或 NULL 处理偏差,排名就会跨组污染——这种问题往往要查到具体某两行的 class_id 字符串末尾空格或编码差异才能定位。











