前两天同事有个 MySQL 数据分组的需求,如下测试数据,需要找出每个 name 分组中 create_date 最近的记录:
成都创新互联长期为上1000家客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为黑山企业提供专业的网站设计、成都网站建设,黑山网站改版等技术服务。拥有十年丰富建站经验和众多成功案例,为您定制开发。
需要注意的是,此处用的 MySQL 是5.6,最初是使用这条语句:
pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;" select name, value, create_date, update_date from t1 group by name order by create_date desc; /pre
用这条 SQL 得到的其实只是每个 name 分组中最先插入的记录,然后按照 create_date 进行了降序排列,和原始需求,完全不同。
此时可采用分而治之的策略,先做排序,再做分组:
pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;" select * from (select name, value, create_date, update_date from t1 order by create_date desc) t group by t.name; /pre
当然,针对此需求,可能有其他方法,有兴趣的朋友,可以尝试写写,共享一下。
可能有细心的朋友会发现个问题,就是上述 SQL 中的 group by ,好像有些奇怪,如果按照常规,select 中的字段需要出现在 group by 中,上述语句竟然没报错?
如果我们在 MySQL 5.7 执行相同的语句:
pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;" select name, value, create_date, update_date from t1 group by name order by create_date desc; /pre
因此从5.6升级到5.7,很可能出现这种相同的 SQL 执行结果不同的现象,这对兼容性测试的要求就会很高,究其原因,一方面是特性决定的,另一方面就是各种配置参数不同导致的。
可以在5.7的 sql_mode 中删除这个 ONLY_FULL_GROUP_BY ,即可达到5.6相同效果了,或者改写 SQL ,例如:
pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;" select * from t1 a where create_date = (select max(create_date) from t1 b where a.name = b.name); /pre
或者,
pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;" select * from t1 a where not exists (select * from t1 b where a.name = b.name and b.create_date a.create_date); /pre
MySQL 8.0支持 row_number()函数,操作应该和如下 Oracle 相近的。
Oracle 中可以使用 row_number()实现此需求:
pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;" select * from (select name, create_date, row_number() over (partition by name order by create_date desc) as r from t1) where r=1; /pre
上一篇聚合函数末尾处使用了GROUP BY,但没有做具体的介绍,这一篇就主要介绍一下GROUP BY的使用方法。顺便介绍一下对分组查询的过滤关键词HAVING的用法。
在MySQL中,GROUP BY关键词可以根据一个或多个字段对查询结果进行分组,类似于Excel中的数据透视表。可以单独使用,但一般情况下都是结合聚合函数来使用的。
语法格式如下:
下面演示都是基于这一张简单的省份对应大区的表格。
【单独使用GROUP BY】
单独使用GROUP BY关键字时,查询结果会只显示每个分组的第一条记录。
根据省份表里面的大区进行聚合,查询全国共分成了几个大区,SQL语句如下↓
【GROUP BY结合聚合函数】
5个聚合函数上一篇已经详细介绍了用法,GROUP BY和聚合函数结合使用也是最频繁的,下面就继续使用省份表来求每个大区有多少个省份,对应的聚合函数就是COUNT函数,SQL语句如下↓
【GROUP BY结合GROUP_CONCAT】
这还是一个很有用的功能,GROUP_CONCAT() 函数会把每个分组的字段值都合并成一行显示出来。
下面继续使用省份表,把每个大区对应的省份放在一行展示,用分号分开,SQL语句如下↓
【GROUP BY结合WITH ROLLUP】
WITH POLLUP关键词用来在所有记录的最后加上一条记录,这条记录是上面所有记录的总和,SQL语句如下↓
【GROUP BY结合HAVING】
在MySQL中,可以使用HAVING关键字对分组后的数据进行过滤。
使用 HAVING 关键字的语法格式如下:
HAVING关键词和WHERE关键词都可以用来过滤数据,且HAVING支持WHERE关键词中所有的操作符和语法。但是WHERE和HAVING关键字也存在以下几点差异:
下面筛选一下省份数量在7个及以上的大区,SQL语句如下↓
【GROUP BY结合ORDER BY】
聚合后的数据,一半情况下也是需要进行排序的,通过ORDER BY对聚合查询结果进行排序,对省份数量按从大到小进行排序,SQL语句如下↓
End
◆ PowerBI开场白
◆ Python高德地图可视化
◆ Python不规则条形图
MySQL GROUP BY 子句
GROUP BY 语句根据一个或多个列对结果集进行分组。在分组的列上我们可以使用 COUNT, SUM, AVG,等函数。
具体语法参考:
from 树懒学堂 - 一站式数据知识平台