MySQL 8.0.2+正式支持ROW_NUMBER()、RANK()、DENSE_RANK()等窗口函数;需用PARTITION BY分组、ORDER BY排序;注意全局ORDER BY不影响窗口内排序逻辑;建议为PARTITION BY和ORDER BY字段建联合索引以提升性能。

MySQL 8.0 窗口函数支持情况确认
MySQL 8.0.2+ 正式支持标准窗口函数,ROW_NUMBER()、RANK()、DENSE_RANK() 都可用;低于 8.0.2 的版本(如 8.0.1)可能报错 FUNCTION xxx does not exist。务必先执行 SELECT VERSION(); 确认版本,再继续。
按班级分组计算成绩排名的正确写法
核心是用 PARTITION BY 划分班级,ORDER BY score DESC 控制降序排位。常见错误是漏写 PARTITION BY,导致全表统一排名而非“班内排名”。
示例语句:
SELECT student_id, class_name, score, ROW_NUMBER() OVER (PARTITION BY class_name ORDER BY score DESC) AS row_rank, RANK() OVER (PARTITION BY class_name ORDER BY score DESC) AS rank_with_ties, DENSE_RANK() OVER (PARTITION BY class_name ORDER BY score DESC) AS dense_rank FROM students;
-
ROW_NUMBER():严格递增,相同分数也不同名次(如 95, 95 → 1, 2) -
RANK():跳过并列后位次(如 95, 95, 90 → 1, 1, 3) -
DENSE_RANK():不跳过(如 95, 95, 90 → 1, 1, 2)
ORDER BY 在窗口函数内外的冲突问题
如果查询末尾带全局 ORDER BY,它不会影响窗口函数内部排序逻辑,但会改变最终结果集顺序——这可能导致你误以为排名出错。例如:
SELECT ..., ROW_NUMBER() OVER (PARTITION BY class_name ORDER BY score DESC) FROM students ORDER BY student_id;
此时排名仍按 score DESC 计算,但输出顺序按 student_id 排,容易看混。建议显式加一层子查询或 CTE 来隔离逻辑:
WITH ranked AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY class_name ORDER BY score DESC) AS rn FROM students ) SELECT * FROM ranked ORDER BY class_name, rn;
性能与索引注意事项
窗口函数本身不走索引,但 PARTITION BY + ORDER BY 字段组合强烈建议建联合索引,否则大表下性能急剧下降。
- 对
students(class_name, score)建 B-tree 索引能显著加速PARTITION BY class_name ORDER BY score DESC - 注意:MySQL 8.0 不支持在索引中指定
DESC,所以score DESC实际仍用升序索引,但优化器可反向扫描,效果接近 - 若查询还含
WHERE class_name = 'A',该索引还能顺便过滤
没索引时,10 万行以上就可能明显卡顿,且 EXPLAIN 会显示 Using filesort 和全表扫描。


















