RANK()支持并列且跳号,按“严格大于当前值的行数+1”计算名次;需配合OVER(ORDER BY)使用,不可用于GROUP BY或WHERE中直接过滤排名,MySQL 8.0.2+才完整支持。

RANK() 就是干这个的,它天然支持并列且跳号——只要值相同,名次就一样,后面直接跳过空缺数字。
为什么 RANK() 会跳号?得看它的计算逻辑
RANK() 不是“数前面有几个人”,而是算“有多少行严格大于当前值”再加 1。比如分数为 95, 95, 90:前两行都只有 0 行严格大于自己,所以都是第 1 名;第三行有 2 行(两个 95)严格大于它,2 + 1 = 3,直接跳到第 3 名。
这和 DENSE_RANK()(返回 1,1,2)和 ROW_NUMBER()(返回 1,2,3)有本质区别,选错函数会导致业务结果错位。
- 体育赛事奖牌榜、考试并列后无“第二名” → 用
RANK() - 班级内档位划分(前 3 名必须连续编号) → 改用
DENSE_RANK() - 生成唯一行号或取固定条数(如每组只取 3 条) → 用
ROW_NUMBER()
RANK() 必须带 OVER,且 ORDER BY 不能省
漏掉 OVER 或其中的 ORDER BY 会直接报错:ERROR 1064 (42000): You have an error in your SQL syntax。它不是聚合函数,不能出现在 GROUP BY 查询里,也不能单独写成 SELECT RANK()。
常见写法:
SELECT student_name, score, RANK() OVER (ORDER BY score DESC) AS ranking FROM student_scores;
- 要按班级分组排名?必须显式加
PARTITION BY class_id,否则全表混排 - 同分时想按考试时间早者优先?写成
ORDER BY score DESC, exam_date ASC - 字段含 NULL?MySQL 默认当最小值(
NULLS FIRST),若希望 NULL 排最后,显式写NULLS LAST(8.0.22+)或提前WHERE score IS NOT NULL
想查“排名 = 2”的记录?别直接 WHERE ranking = 2
窗口函数在 SQL 执行顺序中晚于 WHERE,所以 WHERE ranking = 2 会报错 Unknown column 'ranking'。更关键的是:因为跳号,数据里可能根本不存在第 2 名(比如全是 100,100,90)。
正确做法是用 CTE 或子查询封装:
WITH ranked AS ( SELECT *, RANK() OVER (PARTITION BY category ORDER BY score DESC) AS rk FROM products ) SELECT * FROM ranked WHERE rk = 2;
- 如果目标是“每个分类下前三名”,且允许并列(即可能返回 >3 行),用
RANK()+rk <= 3 - 如果必须严格返回最多 3 条,就得换
ROW_NUMBER() - 大表上注意
ORDER BY字段是否有索引,否则会触发Using temporary; Using filesort,性能骤降
版本和兼容性是硬门槛
RANK() 在 MySQL 8.0.2 及以上才完整支持。低于该版本(包括部分 8.0.1 实例)会报错 FUNCTION xxx.RANK does not exist。执行前务必确认:
SELECT VERSION();
另外,SQL 模式若启用了过于严格的限制(如某些旧兼容模式),也可能禁用窗口函数。临时测试可运行:
SET sql_mode = '';
- 别依赖文档说“8.0 支持”,要看实际小版本号
- 生产环境上线前,必须在同版本实例上用真实数据验证跳号行为是否符合业务预期
- 真正难的不是写出语法,而是判断“这里到底该不该跳号”——这一步错了,后面全错


















