题解 | 找出每个学校GPA最低的同学
找出每个学校GPA最低的同学
https://www.nowcoder.com/practice/90778f5ab7d64d35a40dc1095ff79065
思路分析:
本题的题意明确,就是找出每个学校gpa最小的同学,输出他的device_id,university,gpa。第一眼想到GROUP BY university,然后MIN(gpa),但是关键在于单纯使用分组聚合的话没有办法输出device_id,它只能输出最小的gpa值。所以正确的思路有两种:
- 使用连接:建立一个派生表,在派生表中按如上方式分组聚合找到每个学校的最小gpa,然后将原表和派生表进行内连接,连接条件为学校名称和gpa都相等,这样就能找出每个学校最小gpa的记录,并且因为使用了原表连接,可以输出相应的device_id,如果一个学校中有多个同学gpa都恰好等于最小值,则也能一并输出。
- 使用窗口函数:本题的问题也可以说是属于TOP N问题,因此可以使用窗口函数解决,使用排名函数RANK()或者DENSE_RANK()都行,给每一行分配一个编号,但是注意不能使用ROW_NUMBER(),ROW_NUMBER()的编号是不重复的,假如最低gpa有多名同分同学,ROW_NUMBER()会给这些同分同学依次编号为1、2、3...,最后无法筛选出所有同分同学,而RANK()和DENSE_RANK()都会给相同gpa的同学分配同样的编号。窗口规则设定为按university分组,按gpa升序排序,这样每个学校最低的gpa的同学会被分配序号1,之后取出所有序号为1的同学即可。需要注意窗口函数列没法直接在WHERE子句中使用,因此也需要通过派生表来进行查询。
代码及解释:
由于本题归类在窗口函数知识点中,因此展示窗口函数解题代码:
SELECT device_id, university, gpa FROM ( SELECT device_id, university, gpa, RANK() OVER (PARTITION BY university ORDER BY gpa) AS rk FROM user_profile ) AS t WHERE rk = 1 ORDER BY university;
在FROM子句后面创建派生表,在派生表中使用窗口函数给所有学生排名,窗口规则设定为按university分组:PARTITION BY university、组内按gpa升序排列:ORDER BY gpa,给排序列设置列别名rk,给派生表设置临时表名t,最后在外层查询中限定rk = 1,即可找出每个学校中所有gpa最低的同学。
ORDER BY university在本题中加不加都不影响结果的正确,但在实际业务中通常需要加上ORDER BY子句指定结果集的排序规则,这样可以保证结果集的行顺序一定,可复现。