在业务查询中,我们经常需要统计每个分组内的前几名数据,例如每个班级分数最高的三名学生,或者每个品类销量前三的商品。使用RANK窗口函数配合子查询,可以清晰且高效地实现这个需求。

一、示例表结构
假设有一张学生成绩表 score,字段如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | int | 主键 |
| class_id | int | 班级编号 |
| student_name | varchar | 学生姓名 |
| score | int | 考试成绩 |
二、使用RANK函数计算组内排名
RANK是窗口函数的一种,它可以根据 PARTITION BY 划分组,再按 ORDER BY 在组内排序并生成排名。相同值会获得相同排名,后续排名会跳过。
SELECT
class_id,
student_name,
score,
RANK() OVER (
PARTITION BY class_id
ORDER BY score DESC
) AS rk
FROM score;
三、配合子查询取每组前三名
将上面的查询作为子查询,在外层筛选排名小于等于3的记录,即可得到每组前三名。
SELECT
class_id,
student_name,
score,
rk
FROM (
SELECT
class_id,
student_name,
score,
RANK() OVER (
PARTITION BY class_id
ORDER BY score DESC
) AS rk
FROM score
) t
WHERE t.rk <= 3
ORDER BY class_id, rk;
关于并列情况
如果某个班级第一名有两人同分,RANK会都标为1,第二名为3。此时取 rk <= 3 会返回四行(1、1、3以及原本第三名位置变成4的不会被取)。若希望严格限制行数,可改用 ROW_NUMBER(),但它不会处理并列。
四、完整可运行示例
下面给出包含测试数据的完整脚本,便于理解:
CREATE TABLE score (
id INT,
class_id INT,
student_name VARCHAR(20),
score INT
);
INSERT INTO score VALUES
(1, 1, '张三', 90),
(2, 1, '李四', 90),
(3, 1, '王五', 85),
(4, 1, '赵六', 80),
(5, 2, '钱七', 88),
(6, 2, '孙八', 88),
(7, 2, '周九', 70);
SELECT
class_id,
student_name,
score,
rk
FROM (
SELECT
class_id,
student_name,
score,
RANK() OVER (
PARTITION BY class_id
ORDER BY score DESC
) AS rk
FROM score
) t
WHERE t.rk <= 3
ORDER BY class_id, rk;
五、小结
通过 RANK 窗口函数对数据分组排名,再使用子查询过滤排名,是SQL中解决每组取前三名问题的标准做法。它在各类关系型数据库上均受支持,逻辑直观且易于维护。