RANK() 等窗口函数不能在 WHERE 子句中使用,因其执行顺序晚于 WHERE;正确做法是先用子查询或 CTE 计算排名,再在外部查询中过滤。

WHERE 里直接写 RANK() 就会报错
窗口函数在 SQL 逻辑执行顺序中晚于 WHERE,所以 RANK() OVER (ORDER BY score DESC) 这类表达式不能出现在 WHERE 子句里。常见错误写法:SELECT * FROM scores WHERE RANK() OVER (ORDER BY score DESC) ,会直接报 <code>Invalid use of window function 或字段不存在。
必须用子查询或 CTE 包一层:
SELECT * FROM ( SELECT *, RANK() OVER (ORDER BY score DESC) AS rk FROM scores ) t WHERE rk <= 3;
漏掉这层嵌套是实际开发中最常卡住的地方,尤其在写视图或报表 SQL 时。
RANK() 跳号不是 bug,是它本来的语义
RANK() 的设计就是「并列同名、后续跳号」:两个最高分都是第 1 名,下一个不同值就叫第 3 名(中间的 2 被跳过)。这不是数据出错,而是它按「当前值前面有多少个更优的排序位置 + 并列行数」来算名次。
- 分数 [100, 95, 95, 90] →
RANK()返回 [1, 2, 2, 4] - 想取“销售额前 3 档”,用
WHERE rk 会漏掉 90 那行(因为它的 <code>rk = 4) - 但 90 确实是第四高分,
RANK()在忠实地反映这个断层
如果你发现排名里缺了 2 或 3,别急着调索引或重写 ORDER BY,先确认业务是否真的需要“段位感”——比如竞赛颁奖、考试公示,这种跳号反而是合理表达。
DENSE_RANK() 才能保证档位连续不缺档
当业务要求“按销售额分 A/B/C 三档”,且每档必须有数据、不能跳档时,DENSE_RANK() 是唯一可靠选择。它只在排序值真正变化时才推进名次,相同值复用名次,后续紧接。
- 同样数据 [100, 95, 95, 90] →
DENSE_RANK()返回 [1, 2, 2, 3] -
WHERE drk = 3一定能查到最低一档,不会出现空档 - 注意:
DENSE_RANK()不合并行,只是打标签;返回行数仍是原始行数 - 用
WHERE drk 可能返回远超预期的行(比如 15 人并列第 2 名)
很多分档逻辑崩坏,根源就是用了 RANK() 却期望档位编号连续。
PARTITION BY 写错会让排名彻底歪掉
想做“每个部门内销售额排名”,却只写 RANK() OVER (ORDER BY sales DESC),结果是全表统一排——不是语法错,是语义全错。窗口默认范围是整个结果集,PARTITION BY 必须显式写出。
- 错误:
RANK() OVER (ORDER BY sales DESC)→ 全公司一起排 - 正确:
RANK() OVER (PARTITION BY dept_id ORDER BY sales DESC) -
PARTITION BY字段必须出现在外层 SELECT 中,否则某些数据库(如 PostgreSQL)会静默忽略或报错 - 排序字段含
NULL时行为不一致:SQL Server 默认把NULL当最大值(DESC下排最前),PostgreSQL 默认NULLS LAST。安全做法是显式写ORDER BY COALESCE(sales, 0) DESC或ORDER BY sales DESC NULLS LAST
真正容易被忽略的是:跳不跳号本身不决定对错,关键是你过滤排名的那层逻辑是否和函数语义对齐——用 RANK() 做 Top N 查询,本质是在取“前 N 个段位”,而不是“前 N 行”。

















