RANK()跳号、DENSE_RANK()不跳号是设计意图:RANK()按“段位断层”计算(并列共享名次,下一段名次=当前名次+并列行数),DENSE_RANK()按“档位连续”计算(值变则名次+1);如分数[100,100,95,90],RANK()得[1,1,3,4],DENSE_RANK()得[1,1,2,3]。

RANK() 跳号、DENSE_RANK() 不跳号,不是 bug,是设计逻辑的直接体现——前者按“段位断层”算,后者按“档位连续”算。
为什么 RANK() 会突然跳号?
RANK() 的计算方式是:当前名次 + 并列行数 = 下一名次。它把相同值看作一个“排名段”,段内共享名次,段与段之间留空档,用空缺来体现实际差距。
- 分数
[100, 100, 95, 90]→RANK()结果是[1, 1, 3, 4](两个第一之后,下一个不同分值直接叫“第三名”) - 常见错误现象:
RANK()返回1, 1, 3, 4,有人误以为第 3 行“丢了名次”或“结果错乱”——其实它准确表达了:前两人同属第一段,第三名属于第二段(中间缺了“第二段”的编号,因为没人单独占这一档) - 在 MySQL 8.0+、PostgreSQL、SQL Server 中行为完全一致;MySQL 5.7 及更早版本不支持,会报
ERROR 1064 (42000)
为什么 DENSE_RANK() 名次连续?
DENSE_RANK() 的逻辑更直白:只要值变化,名次就 +1。它不关心前面并列了几行,只关心“这是第几档”。
- 同样分数
[100, 100, 95, 90]→DENSE_RANK()结果是[1, 1, 2, 3] - 使用场景明确:
DENSE_RANK()适合做固定档位划分,比如“销售额分 A/B/C 三档”,必须有且仅有三档,不能缺也不能多 - 注意:它不会合并行,
score = 100的两人仍各自输出,dense_rank值都是1 - 误用
WHERE dense_rank 查 Top 2,可能返回 10 行(比如 8 人并列第 2 名),远超预期
RANK() 和 DENSE_RANK() 都不能在 WHERE 中直接用
窗口函数是在 SELECT 阶段才计算的,而 WHERE 执行在 SELECT 之前。以下写法一定报错:
SELECT * FROM sales WHERE RANK() OVER (ORDER BY amount DESC) <= 3;
正确做法必须用子查询或 CTE:
SELECT * FROM ( SELECT *, RANK() OVER (ORDER BY amount DESC) AS rnk FROM sales ) t WHERE rnk <= 3;
- 漏掉这层嵌套是新手最常踩的坑,报错信息通常是
Invalid use of window function或字段不存在 - 如果还用了
PARTITION BY,比如按部门排名,却漏写PARTITION BY dept,会导致全校/全表统一排序,业务语义彻底歪掉
真正容易被忽略的点
跳不跳号本身不决定对错,而是由业务是否允许“空档”来驱动。比如奥运奖牌榜要体现“并列金牌后下一个是铜牌”,就得用 RANK();而内部绩效划档要求“S/A/B/C 四档必须满额”,就必须用 DENSE_RANK()。选错函数不是语法问题,是业务语义理解偏差。

















