rank()跳号而dense_rank()不跳号:rank()按值分段并累加段长定名次,如[100,100,95,90]得[1,1,3,4];dense_rank()按不同值序号递增,得[1,1,2,3];二者均不可在where中直接使用。

RANK() 和 DENSE_RANK() 的区别不在实现,而在语义:一个跳号,一个不跳号——这是设计选择,不是 bug。
为什么 RANK() 返回 1,1,3,4 而 DENSE_RANK() 返回 1,1,2,3
RANK() 把相同值看作一个“段”,段内共享名次,下一段名次 = 当前段名次 + 段长度;DENSE_RANK() 只关心“这是第几个不同值”,值一变就 +1,不管前面并列几行。
- 分数
[100, 100, 95, 90]→RANK()是[1, 1, 3, 4](两个第一,没人占第二档,直接叫第三名) - 同组数据 →
DENSE_RANK()是[1, 1, 2, 3](第一档、第二档、第三档,档位连续) - 常见错误现象:
RANK()结果里缺了第 2 名,业务方以为漏数据——其实是 95 分属于第二段,但这一段被命名为 3,反映的是“断层感”
WHERE 中直接用 RANK() 或 DENSE_RANK() 会报错
窗口函数在 SELECT 阶段才计算,而 WHERE 执行在之前。所以 WHERE RANK() OVER (ORDER BY amount DESC) 一定失败,报错通常是 <code>Invalid use of window function 或字段不存在。
- 正确写法必须套一层子查询或 CTE:
SELECT * FROM ( SELECT *, RANK() OVER (ORDER BY amount DESC) AS rnk FROM sales ) t WHERE rnk
- 漏掉这层嵌套是新手最高频的坑
- 如果还用了
PARTITION BY dept,却漏写PARTITION BY,会导致全表统一排序,业务逻辑彻底歪掉
RANK() 和 DENSE_RANK() 都不能替代唯一编号
只要排序字段值相同,两者都必然并列——它们不解决“同分怎么排先后”的问题。
- ORDER BY 字段含重复值(比如多人同分),又没加兜底字段(如
id或created_at),结果可能非确定:这次张三在前,下次李四在前 - 安全写法是在
ORDER BY末尾补唯一字段,例如:ORDER BY score DESC, id ASC -
ROW_NUMBER()才是强制唯一编号的选项,但它不反映业务上的“并列”含义
MySQL 5.7 及更早版本根本不支持这两个函数
在 MySQL 5.7 上执行含 OVER 的语句,会直接报错:ERROR 1064 (42000): You have an error in your SQL syntax。
- MySQL 8.0+、PostgreSQL、SQL Server 均支持,行为一致
- 如果目标环境兼容性存疑,得先查版本:
SELECT VERSION(); - 别指望低版本靠模拟(比如自连接或变量)能稳定复现窗口语义,尤其在并发或大数据量下容易出错
真正容易被忽略的点是:跳不跳号本身不改变性能,也不影响语法,但它直接决定下游业务逻辑是否成立——选错函数,轻则报表对不上,重则奖金发错档位。










