RANK() 返回1,1,3,4而DENSE_RANK()返回1,1,2,3,因RANK()按相等值分段跳名次(100,100占第1段,下段从3起),DENSE_RANK()按不同值序号连续编号(100、95、90对应1、2、3)。

为什么 RANK() 返回 1,1,3,4 而 DENSE_RANK() 返回 1,1,2,3
RANK() 把相同排序值看作一个“段”,段内所有行共享名次,下一段名次 = 当前段名次 + 当前段行数;DENSE_RANK() 只关心“这是第几个不同值”,值一变就 +1。
比如分数 [100, 100, 95, 90] 降序排列:
-
RANK():两个 100 占第 1 段(长度 2),所以第 3 行从名次 3 开始 →[1, 1, 3, 4] -
DENSE_RANK():100 是第 1 个不同值,95 是第 2 个,90 是第 3 个 →[1, 1, 2, 3]
这不是计算错误,是语义设计差异:RANK() 强调“断层感”,DENSE_RANK() 强调“档位连续”。
WHERE 中直接用 RANK() 或 DENSE_RANK() 一定报错
窗口函数在 SELECT 阶段才执行,而 WHERE 在它之前就已过滤完毕。以下写法必然失败:
SELECT * FROM sales WHERE RANK() OVER (ORDER BY amount DESC) <= 3;
报错信息通常是 Invalid use of window function 或字段不存在。
正确做法必须套一层子查询或 CTE:
SELECT * FROM ( SELECT *, RANK() OVER (ORDER BY amount DESC) AS rnk FROM sales ) t WHERE rnk <= 3;
漏掉这层嵌套是新手最高频的坑,和选哪个函数无关,但一旦出错,容易误判为函数本身不可用。
PARTITION BY 写错会导致分组排名完全跑偏
想按部门排销售榜,却漏写 PARTITION BY dept,结果变成全公司统一排序,不是每部门独立算 Top 3。
还有几个关键细节容易被忽略:
-
PARTITION BY字段必须出现在子查询的SELECT列表中,否则外部无法对齐分组(多数数据库会报错或静默忽略) - 如果排序字段含重复值(如多人同分),不加兜底字段(如
id或created_at)会导致排名不稳定 - 安全写法是:
ORDER BY score DESC, id ASC;错误写法是仅写ORDER BY score DESC
选 RANK() 还是 DENSE_RANK() 取决于业务是否允许“空档”
函数选错不会报错,但业务逻辑会悄悄歪掉:
- 用
RANK()查 “Top 10” 可能只返回 7 行:因为名次不连续(如只有 1,1,3,4,6…),凑不满 10 个不同名次 - 用
DENSE_RANK()做 “前 2 档” 可能返回 15 行:只要档位 ≤ 2,不管每档多少人 - 报表写 “A档(第1名)、B档(第2名)、C档(第3名)”,
DENSE_RANK()输出天然匹配;RANK()的1,1,3容易引发业务方追问:“第2名去哪了?”
真正容易被忽略的点是:跳不跳号本身不改变性能或语法兼容性,但它直接决定你写的 WHERE dense_rank 是不是真能代表“前 N 档”——而这个判断,只能靠业务语义,不能靠直觉。

















