rank()跳号是因其语义为“并列同名次且后续名次跳过被占位数”,如分数100、95、95、88对应排名1、2、2、4;dense_rank()不跳号,因它按“值变则名次+1”计算,同例结果为1、2、2、3。

RANK() 跳号,DENSE_RANK() 不跳号——这不是风格偏好,而是业务语义是否允许“空档”的硬性选择。
为什么 RANK() 的结果里突然少了第 3 名
根本原因是并列导致名次跳过。比如分数为 100、95、95、88 的四人,RANK() OVER (ORDER BY score DESC) 返回的是 1、2、2、4——第 3 名被跳掉了。
- 这不是 bug,是设计行为:
RANK()把相同值看作一个“段”,段内共享名次,段与段之间留空档,体现实际差距 - 常见误判:看到
rank = 4就以为是“第四名”,其实它准确表达的是“第四高分”,但名次编号上跳过了第 3 档 - 如果用
WHERE rank 查 Top 3,<code>score = 88那行会被漏掉——它确实是第四高分,但名次编号是 4
DENSE_RANK() 怎么保证名次连续不跳
DENSE_RANK() 的逻辑更直白:只要排序值变化,名次就 +1,不管前面并列了几行。
- 上例中返回
1、2、2、3,后续所有名次都紧贴前一个 - 适用场景明确:按销售额分 A/B/C 三档,必须有且仅有三档,不能缺档也不能多档
- 注意陷阱:它不会合并行——
score = 95的两人仍各自占一行,dense_rank值都是2 - 如果用
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) <p>正确做法必须用子查询或 CTE:</p><pre class="brush:php;toolbar:false;">SELECT * FROM ( SELECT *, RANK() OVER (ORDER BY amount DESC) AS rnk FROM sales ) t WHERE rnk
- 漏掉这层嵌套是新手最常踩的坑,报错信息通常是
Invalid use of window function或字段不存在 - 如果还用了
PARTITION BY(比如按部门排名),却漏写PARTITION BY dept,会导致全校/全表统一排序,业务语义彻底歪掉
真正容易被忽略的一点是:跳不跳号本身不决定你该用哪个——决定因素是你想表达什么业务规则。名次编号只是标签,真正取哪几行,永远靠外层的 WHERE、QUALIFY 或聚合逻辑控制。










