MySQL:如何用子查询按班级和课程对学生成绩排名
按班级和课程分组排名(仅用子查询)
现有表结构及数据
表名:Scores
| student_id | class_id | course | score |
|---|---|---|---|
| 1 | 1 | 1 | 80 |
| 2 | 1 | 1 | 80 |
| 3 | 2 | 3 | 75 |
| 4 | 3 | 2 | 90 |
| 5 | 1 | 2 | 85 |
| 6 | 2 | 3 | 85 |
| 7 | 2 | 3 | 85 |
| 8 | 3 | 4 | 78 |
| 9 | 3 | 4 | 76 |
| 10 | 3 | 4 | 79 |
查询需求
- 查询字段:
student_id、class_id、course、score以及rank(排名字段) - 排名规则:按
class_id和course分组排名,同班级同课程内按分数降序、student_id降序排序(分数相同时,student_id更大的排名靠前) - 限制条件:仅允许使用子查询,不能使用
rank()等窗口/分区函数 - 结果排序:按
class_id降序、course降序、score降序、student_id降序排列
预期结果
| student_id | class_id | course | score | rank |
|---|---|---|---|---|
| 10 | 3 | 4 | 79 | 1 |
| 8 | 3 | 4 | 78 | 2 |
| 9 | 3 | 4 | 76 | 3 |
| 4 | 3 | 2 | 90 | 1 |
| 7 | 2 | 3 | 85 | 1 |
| 6 | 2 | 3 | 85 | 2 |
| 3 | 2 | 3 | 75 | 3 |
| 5 | 1 | 2 | 85 | 1 |
| 2 | 1 | 1 | 80 | 1 |
| 1 | 1 | 1 | 80 | 2 |
当前困境
能实现全局排名,但无法按相同class_id和course分组进行排名。
解决方案
核心思路是通过子查询统计同一班级、同一课程中,分数高于当前记录,或分数相同但student_id更大的记录数量,该数量加1即为当前记录的排名。
对应的SQL语句:
SELECT s1.student_id, s1.class_id, s1.course, s1.score, ( SELECT COUNT(*) + 1 FROM Scores s2 WHERE s2.class_id = s1.class_id AND s2.course = s1.course AND (s2.score > s1.score OR (s2.score = s1.score AND s2.student_id > s1.student_id)) ) AS rank FROM Scores s1 ORDER BY s1.class_id DESC, s1.course DESC, s1.score DESC, s1.student_id DESC;
逻辑说明
- 外层查询遍历
Scores表的每一条记录(记为s1) - 内层子查询针对每条
s1记录,统计满足以下条件的s2记录数:- 与
s1同班级、同课程 - 分数高于
s1,或分数相同但student_id更大(匹配预期结果中分数相同时大ID靠前的规则)
- 与
- 统计结果加1即为当前记录的排名——因为有N条记录比它靠前,它的排名就是N+1
- 最后按要求的排序规则输出结果
内容的提问来源于stack exchange,提问作者VampireC
相关产品推荐
相关产品推荐

