今天写SQL时,发现在使用GROUP BY 分组查询时,SELECT 查询字段包含非分组字段会导致该字段信息不匹配问题,写以此篇记录,并介绍一种解决方法。

      问题描述:

        在牛客SQL33 找出每个学校GPA最低的同学问题中,该题有如下字段和数据的user_profile表格:

         要求找到每个学校gpa最低的同学,并以以下方式输出:

      个人思路(错误) 

        我首先想到的就是先使用group by按照学校将数据进行分组,然后再使用min函数就能获取到每个学校gpa最低的同学,即如下代码:

SELECT `device_id`,`university`,MIN(`gpa`)
FROM `user_profile`
GROUP BY `university`
ORDER BY `university` ASC;

        但是我发现它在运行时就会产生错误,经查询发现,学校和学生是一对多的关系,当在select中的字段既不是分组字段,又不是聚合函数时,使用这种查询方式会将多个同学的这个同一字段的所有信息聚合成一条,这一条信息从所有信息中随机抽取,如下所示:

         这个device_id和gpa是每一个学校,随机抽取一个同学的一条记录作为的数据,然后此时用min函数查找每个学校gpa最小的同学,虽然我们能得到最小的gpa,但是最小gpa同学的device_id和聚会后学校中的device_id不一定能对应上(这里它只是根据学校分组将每个学校的最小gpa查询出来,并在分组表中将最小的gpa作为gpa字段,但是分组表中device_id它并不会根据最小gpa进行更换,它还是原来的device_id),可通过下图看出差别:

 

        我们可以看到北京大学最小gpa虽然得到了,但是device_id却对应不上。这就是因为一对多时,select后的字段使用非分组字段(非聚合函数)造成的该字段的数据聚合,最后只保留一份随机字段值,造成数据不匹配。

      解决方式:

        为了解决这个问题,我们通常有一种方式解决:

       1、子查询

        先通过子查询查找到每个学校的最低gpa,此子查询只包含university和MIN(gpa)字段,因为两者是分组字段和聚合函数,并不会造成数据聚合,所以能得到正常的每个学校对应的最低gpa,然后将子查询得出的数据与原表格中的数据进行内连接,那么就只会剩下与子查询匹配的数据,此时该数据就包含device_id、university和最低gpa字段了(这里也可以通过滑动窗口实现)

SELECT a.device_id,a.university,a.gpa 
FROM user_profile a
JOIN (
    SELECT university,min(gpa) gpa 
    FROM user_profile 
    GROUP BY university
     ) b
on a.university=b.university and a.gpa=b.gpa
ORDER BY university;

        但是如果同一个学校存在多个相同的最低gpa会怎么办,这个问题我还没解决。

        总结:

        在使用分组查询时,select后面的字段要么是分组字段,要么是聚合函数

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐