RANK()跳号是因为其设计逻辑是“并列即同名、名次不连续”,遇相同值赋予同一名次,下一名次=当前名次+并列行数,如[95,95,90]得[1,1,3];它反映“多少人排在你前面”,非简单序号,故直接用于分页或取前N名易漏数据。

RANK() 为什么总跳号?
RANK() 遇到相同值时给它们同一名次,但下一名次 = 当前名次 + 并列行数。比如成绩 [95, 95, 90],结果是 [1, 1, 3]——不是 bug,是设计行为。它反映的是「有多少人排在你前面」,而不是「你是第几个被列出的」。
常见错误是拿 RANK() 直接做分页或“取前 N 名”,结果漏掉数据:比如两个第 2 名后是第 4 名,WHERE rnk 就会漏掉那个第 4 名实际该进 Top 3 的人。
- 要并列且接受跳号 → 用
RANK() - 要并列但不跳号 → 改用
DENSE_RANK() - 要严格连续编号(哪怕值相同也不同号)→ 用
ROW_NUMBER()
怎么写才能按部门分别排名?
PARTITION BY 不是可选的——漏写就变成全表统一排名,不是每个部门独立算。典型错误是把它和 GROUP BY 混,或者误以为可以写在 WHERE 后面(语法不允许)。
正确写法必须放在 OVER() 里,顺序固定:OVER (PARTITION BY dept_id ORDER BY salary DESC)。注意 dept_id 类型要和 SELECT 中一致,否则可能隐式转换失败;如果该字段含 NULL,多数数据库(MySQL/PostgreSQL)会把所有 NULL 视为单独一组。
- 分区字段必须出现在 SELECT 列表中(MySQL 8.0+ 强制要求)
- 别在
ORDER BY里直接写复杂表达式(如LEN(name)),SQL Server 可能报错 - 想跨部门全局排名?删掉
PARTITION BY,只留ORDER BY
为什么 WHERE 里不能直接用 RANK() 别名?
窗口函数执行时机在 WHERE 和 HAVING 之后、ORDER BY 之前,所以 WHERE rnk 会报错:<code>Unknown column 'rnk' in WHERE clause。
必须用子查询或 CTE 包一层:
SELECT emp_name, dept_id, salary
FROM (
SELECT emp_name, dept_id, salary,
RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rnk
FROM employees
) t
WHERE rnk <= 2;
- 别在聚合查询里混用窗口函数和普通聚合(如
SELECT AVG(score), RANK() OVER ()),会报错 - CTE 写法更清晰,尤其多层嵌套时
- 如果只是取 Top N,加
LIMIT或TOP能缓解性能压力
NULL 值怎么影响 RANK() 结果?
默认行为因库而异:ORDER BY score DESC 时,MySQL 把 NULL 排最前,PostgreSQL 默认 NULLS LAST。这会导致 NULL 行意外占了个名次,甚至和非空值并列。
显式控制更安全:
- PostgreSQL/Oracle:用
ORDER BY score DESC NULLS LAST - MySQL/SQL Server:用
ORDER BY CASE WHEN score IS NULL THEN 1 ELSE 0 END, score DESC - 业务上不想让
NULL参与排名?先在WHERE过滤掉,否则它仍会占据一个排名位置
真正容易被忽略的点是:PARTITION BY 和 ORDER BY 是绑定关系,改其中一个就得同步检查另一个——比如加了 PARTITION BY dept_id 却忘了调整 ORDER BY 字段粒度,排名可能就失去业务意义了。

















