RANK()跳号而DENSE_RANK()不跳号是因业务语义不同:RANK()按值段跳名次(如1,1,3,4),DENSE_RANK()按去重档位递增(如1,1,2,3);二者均不可在WHERE中使用,因窗口函数在SELECT阶段执行。

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 就以为漏数据——其实是业务上真没有“唯一第二名”
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列表中,否则外部无法对齐分组(多数数据库会报错或静默忽略) - ORDER BY 字段含重复值时(如多人同分),不加兜底字段(如
id或created_at)会导致排名不稳定 - 安全写法:
ORDER BY score DESC, id ASC;错误写法:ORDER BY score DESC(无保序)
选 RANK() 还是 DENSE_RANK() 取决于业务语义
函数选错不会报错,但业务逻辑会悄悄歪掉。
- 用
RANK()查 “Top 10” 可能只返回 7 行:因为排名数字不连续,凑不满 10 个不同名次 - 用
DENSE_RANK()做 “前 2 档” 可能返回 15 行:只要档位 ≤ 2,不管每档多少人 -
RANK()适合奥运奖牌榜、淘汰制绩效(强调“击败了多少人”) -
DENSE_RANK()适合客户分档、BI 看板、Excel 对齐(要求档位连续、数量可控)
真正容易被忽略的点是:跳不跳号本身不改变数据,但会彻底改变你用 WHERE 过滤时的实际行数——而且 MySQL 5.7 及更早版本根本不支持这两个函数,直接报 ERROR 1064。

















