1. 问题描述
有一个存储两个班级学生成绩的数据表 students,字段如下
id | name (姓名) | class (班级) | score (成绩) |
---|---|---|---|
1 | tom | 1 | 90 |
2 | jerry | 2 | 92 |
3 | jack | 1 | 87 |
4 | rose | 2 | 95 |
想一次性返回每个班上的第一名?
2. 问题思考
- 因为要返回两不同班级中的第一名,所以首先要按班级分组,然后在分组内按成绩排序;
- SQL 中分组操作的常用指令是 group by
- 问题在与 group by 函数用于对某类相同性质做聚类分析,例如统计总数,是忽略个条记录具体数据的。
2. 解决方法
要想返回具体记录,需要使用 partition by (分区函数) 来实现。该函数会对数据按某个属性划分区域,并保留每条记录的具体数据。用户可以对分区后的数据再做进一步处理。
WITH groups AS (
SELECT "id", "name", "class", "score",
ROW_NUMBER() OVER (PARTITION BY "class" order by "score" DESC) AS "rownum"
FROM "students"
)
SELECT * FROM groups WHERE groups.rownum = 1;
- SQL 语句中先通过 Partition by 对数据按班级号 class 分区(也就是分组,但没有做聚合);
- 然后对分区内的数据记录按成绩字段 score 进行降序排序;
- 通过 ROW_NUMBER() 函数给每条记录添加组内的一个序号字段 rownum;
- 最后从每组数据里取出序号第一的学生记录;
3. 结论
SQL 语言非常强大,在保持SQL标准和跨数据库的原则上,能用SQL写的尽量用SQL,比写代码简洁易懂。