RANK()并列后跳名次(如1,1,3),DENSE_RANK()并列后不跳(如1,1,2);多条件排序时仅主字段决定并列,辅字段仅用于破 ties 且须置于ORDER BY末尾;WHERE不可直接引用窗口函数别名rk。

名次并列时RANK()和DENSE_RANK()到底差在哪
直接说结论:遇到相同分数要并列名次,且后续名次要“跳过”(比如两个第1名后直接第3名),用 RANK();如果希望并列后紧接下一名(两个第1名后是第2名),得换 DENSE_RANK()。很多人一上来就套 RANK(),结果发现排名断层严重,其实是没搞清业务对“跳位”是否接受。
典型场景:学生成绩表里多个95分,都该算第1名;但若按总分排班级前10,校方要求“并列即占位”,这时 RANK() 才合规;而做内部绩效分级时,可能更倾向用 DENSE_RANK() 避免层级空档。
注意点:
-
RANK()的排序逻辑是“值相等 → 名次相同 → 下一名次 = 当前名次 + 相同值个数”,所以容易出现 1,1,3,4 这种序列 - 它不关心字段是否唯一,只认
ORDER BY表达式的计算结果是否重复 - 如果
ORDER BY后跟了多个字段(如ORDER BY score DESC, submit_time ASC),只要组合值重复,仍会并列
多条件排序下RANK()的ORDER BY怎么写才不翻车
多条件名次计算不是简单堆字段,关键在「主排序决定名次依据,辅排序只破 ties(平局)」。比如既要按销售额排名,又想同销售额时按客户数升序排列——那销售额是主条件,客户数只是辅助打散顺序,不影响并列判定。
错误写法:RANK() OVER (ORDER BY sales DESC, customer_count ASC) —— 这会导致哪怕 sales 相同,customer_count 不同也会被当成不同名次,彻底废掉并列逻辑。
正确做法是明确区分:
- 把真正决定“是否并列”的字段放
ORDER BY最前面,且**不要混入破 ties 字段** - 如需稳定排序(避免每次执行结果顺序不一致),再在末尾加破 ties 字段,但它不参与并列判断
- 示例:同销售额并列,但输出时让新客户优先 →
RANK() OVER (ORDER BY sales DESC) AS rk,再用ORDER BY sales DESC, new_customers DESC控制最终展示顺序
WHERE里不能直接过滤RANK()结果?这是SQL执行顺序坑
写 SELECT *, RANK() OVER (...) AS rk FROM t WHERE rk 肯定报错:<code>column "rk" does not exist。因为 WHERE 执行在窗口函数计算之前,此时 rk 根本还没诞生。
必须用子查询或 CTE 搬一层:
SELECT * FROM ( SELECT *, RANK() OVER (ORDER BY score DESC) AS rk FROM students ) t WHERE t.rk <= 10;
常见误操作:
- 试图在
HAVING里用RANK()—— 窗口函数不能出现在HAVING,那是聚合函数的地盘 - 在 UPDATE 或 DELETE 中直接引用窗口函数别名 —— 大多数数据库不支持,得先落临时表或用 JOIN 模拟
- 嵌套窗口函数,比如
RANK() OVER (ORDER BY RANK() OVER (...))—— 语法合法但毫无意义,还拖慢性能
MySQL 5.7不支持RANK()?降级方案得看版本
MySQL 5.7 及更早版本确实没有原生 RANK(),强行用会报 FUNCTION xxx.RANK does not exist。别急着升级,有兼容写法:
核心思路:用变量模拟排名计数器,配合 IF 判断前一行值是否变化:
SELECT name, score, @rank := IF(@prev = score, @rank, @rank + @count) AS rk, @count := IF(@prev = score, @count + 1, 1) AS cnt, @prev := score FROM students, (SELECT @rank := 0, @count := 0, @prev := NULL) r ORDER BY score DESC;
但要注意:
- 变量赋值顺序依赖
ORDER BY执行路径,MySQL 8.0+ 已声明该行为“不保证可靠”,生产环境慎用 - 如果数据量大、并发高,变量方式可能因执行计划变动导致错位
- 真正稳妥的降级方案是:用自关联 COUNT,例如
(SELECT COUNT(DISTINCT s2.score) FROM students s2 WHERE s2.score >= s1.score),虽慢但语义清晰、全版本兼容
真正难的不是写出 RANK(),而是想清楚“并列是否该占位”“下游系统能否接受断层名次”“要不要为兼容老版本牺牲可读性”。这些决策点,比语法本身影响更大。

















