SQL使用GROUP BY分组排序问题

当我们想要查询一个单表或者想要JOIN一个表的数据,且需要使用GROUP BY来进行分组时,发现分组之后的顺序又不是我们想要的顺序,怎么办?

例子

测试数据:
SELECT * FROM lesson l ORDER BY l.course_id;

SQL使用GROUP BY分组排序问题_第1张图片

上面是一个用于测试的数据表,一个course_id对应多个lesson_id,且每个lesson都有自己的start_time。如果我们直接使用GROUP BY把数据根据course_id来进行分组,那么lesson的信息,默认取的是插入时的第一条数据。
直接分组:
SELECT l.course_id,l.start_time FROM lesson l GROUP BY l.course_id;

SQL使用GROUP BY分组排序问题_第2张图片

这里我们可以结合第一张图来看下,GROUP BY之后,start_time字段的数据就是对应该course_id的第一条数据的时间(逻辑有点绕)。如果我们想要取最早的start_time,我们就要结合ORDER BY,但是MySQL在处理GROUP BY之前,是不会按照我们的ORDER BY先进性排序的。所以,我们看下面这个方法。
使用LIMIT固定数据排序:
SELECT l.course_id,l.start_time FROM (SELECT * FROM lesson l1 ORDER BY l1.start_time ASC LIMIT 10000) l GROUP BY l.course_id;

SQL使用GROUP BY分组排序问题_第3张图片

注意:这里LIMIT后面的数值要大于表数据总量。
这个语句中我们先根据start_time排序出来一个临时表,且加上LIMIT来固定数据顺序(如果不加LIMIT,GROUP BY依然不会使用我们自定义的排序),之后再分组这个临时表,即可达到我们的目的。
结合图1可以看出,分组之后的start_time已经是对应的course_id的最早那个数据。
使用GROUP_CONCAT来拿到想要的start_time
SELECT l.course_id,MIN(l.start_time) AS start_time FROM lesson l GROUP BY l.course_id;

SQL使用GROUP BY分组排序问题_第4张图片

这个方法的查询结果和上面是一样的。我们先按照course_id分组,之后用MIN函数取出多个start_time中最小的一个,即可达成展示course_id对应最早start_time的目的。
但这个方法做的是字段聚合,如果我们查询的时候把主键也展示出来,我们就能看出区别了。
方法对比:
SELECT l.lesson_id,l.course_id,l.start_time FROM (SELECT * FROM lesson l1 ORDER BY l1.start_time ASC LIMIT 10000) l GROUP BY l.course_id;
SELECT l.lesson_id,l.course_id,MIN(l.start_time) AS start_time FROM lesson l GROUP BY l.course_id;
result1:

SQL使用GROUP BY分组排序问题_第5张图片

result2:

SQL使用GROUP BY分组排序问题_第6张图片

叮!!!在这两个对比中,我们发现start_time所对应的主键,是不一样的,因为下面的这个SQL是在字段上做了聚合处理,实际上start_time对应的真实主键是上面那个SQL执行的结果。如果不考虑这个因素,在数据量较大的情况下,下面的方法要快于上面的方法。
想要深入了解GROUP BY使用时涉及到的排序问题,还需要了解一下MySQL是如何处理GROUP BY的。
这篇博客仅作为笔记,不能够作为很好的指南。深究该问题,请多多查阅MySQL官方文档才是最靠谱的方法。
相关文档连接:MySQL Handling of GROUP BY
最后翻译原文一段话:
Furthermore, the selection of values from each group cannot be influenced by adding an ORDER BY clause.
Result set sorting occurs after values have been chosen, and ORDER BY does not affect which value within each group the server chooses.
Disabling ONLY_FULL_GROUP_BY is useful primarily when you know that, due to some property of the data, all values in each nonaggregated column not named in the GROUP BY are the same for each group.
此外,通过添加ORDER BY子句不能影响从每个组中选择值。
选择值之后,将进行结果集排序,并且ORDER BY不会影响服务器在每个组中选择哪个值。
禁用ONLY_FULL_GROUP_BY非常有用,主要是因为您知道由于数据的某些属性,每个未聚合列中未在GROUP BY中命名的所有值对于每个组都是相同的。
这里提到了ONLY_FULL_GROUP_BY,当我们直接使用GROUP_BY却不在语句中使用聚合函数的话,需要禁用该选项(貌似大部分人都选择禁用了)。

你可能感兴趣的:(iCode)