SQL180 每类试卷得分前3名

发布于:2025-08-13 ⋅ 阅读:(18) ⋅ 点赞:(0)

描述

现有试卷信息表examination_info(exam_id试卷ID, tag试卷类别, difficulty试卷难度, duration考试时长, release_time发布时间):

id exam_id tag difficulty duration release_time
1 9001 SQL hard 60 2021-09-01 06:00:00
2 9002 SQL hard 60 2021-09-01 06:00:00
3 9003 算法 medium 80 2021-09-01 10:00:00


试卷作答记录表exam_record(uid用户ID, exam_id试卷ID, start_time开始作答时间, submit_time交卷时间, score得分):

id uid exam_id start_time submit_time score
1 1001 9001 2021-09-01 09:01:01 2021-09-01 09:31:00 78
2 1001 9001 2021-09-01 09:01:01 2021-09-01 09:31:00 81
3 1002 9002 2021-09-01 12:01:01 2021-09-01 12:31:01 81
4 1003 9001 2021-09-01 19:01:01 2021-09-01 19:40:01 86
5 1003 9002 2021-09-01 12:01:01 2021-09-01 12:31:51 89
6 1004 9001 2021-09-01 19:01:01 2021-09-01 19:30:01 85
7 1005 9003 2021-09-01 12:01:01 2021-09-01 12:31:02 85
8 1006 9003 2021-09-07 10:01:01 2021-09-07 10:21:01 84
9 1003 9003 2021-09-08 12:01:01 2021-09-08 12:11:01 40
10 1003 9002 2021-09-01 14:01:01 (NULL) (NULL)

找到每类试卷得分的前3名,如果两人最大分数相同,选择最小分数大者,如果还相同,选择uid大者。由示例数据结果输出如下:

tag

uid ranking
SQL 1003 1
SQL 1004 2
SQL 1002 3
算法 1005 1
算法 1006 2
算法 1003 3


解释:有作答得分记录的试卷tag有SQL和算法,SQL试卷用户1001、1002、1003、1004有作答得分,最高得分分别为81、81、89、85,最低得分分别为78、81、86、40,因此先按最高得分排名再按最低得分排名取前三为1003、1004、1002。

SELECT
    tag,
    uid,
    ranking
FROM
    (
        SELECT
            ei.tag,
            er.uid,
            MAX(er.score) AS max_score, -- 每个用户在该类别下的最高分
            MIN(er.score) AS min_score, -- 最低分(用于平局决胜)
            ROW_NUMBER() OVER (
                PARTITION BY
                    ei.tag
                ORDER BY
                    MAX(er.score) DESC, -- 先按最高分排序
                    MIN(er.score) DESC, -- 再按最低分排序
                    er.uid DESC -- 最后按 uid 排序
            ) AS ranking
        FROM
            examination_info ei
            JOIN exam_record er USING (exam_id)
        WHERE
            er.score IS NOT NULL -- 只统计有得分的记录
        GROUP BY
            ei.tag,
            er.uid -- 按“类别+用户”分组
    ) t
WHERE
    ranking <= 3 -- 取前3名
ORDER BY
    tag,
    ranking;

窗口函数

1. 窗口函数基本格式

函数名() OVER (
    PARTITION BY 分组字段   -- 按什么分组(可选)
    ORDER BY 排序字段       -- 按什么排序(必选)
) 

👉 就像“分组 + 排序 + 计算”,但不改变行数


2. 常用函数

函数 作用
ROW_NUMBER() 排名:1,2,3,4…(不并列)
RANK() 排名:1,2,2,4…(并列,跳号)
DENSE_RANK() 排名:1,2,2,3…(并列,不跳号)

3. 举个例子

ROW_NUMBER() OVER (
    PARTITION BY class 
    ORDER BY score DESC
)

意思:

  • 每个班(class)单独排名
  • 按分数从高到低排
  • 返回排名:第1名、第2名…

4. 注意!

  • 窗口函数不能直接在 WHERE 里用
  • 要用的话,得套一层子查询
SELECT * FROM (
    SELECT ..., ROW_NUMBER() OVER (...) AS rn
    FROM table
) t
WHERE rn <= 3;  -- ✅ 这样才能用

✅ 一句话记住

PARTITION BY 分组,ORDER BY 排序,ROW_NUMBER() 给每行打上排名。