用绝对通俗易懂的大白话解释清楚sql中的group by

先来看下表1,表名为test:

 

表1

  执行如下SQL语句:

SELECT name FROM test GROUP BY name;

你应该很容易知道运行的结果,没错,就是下表2:

表2

可是为了能够更好的理解“group by”多个列“”聚合函数“的应用,我建议在思考的过程中,由表1到表2的过程中,增加一个虚构的中间表:虚拟表3。下面说说如何来思考上面SQL语句执行情况:

1.FROM test:该句执行后,应该结果和表1一样,就是原来的表。

2.FROM test Group BY name:该句执行后,我们想象生成了虚拟表3,如下所图所示,生成过程是这样的:group by name,那么找name那一列,具有相同name值的行,合并成一行,如对于name值为aa的,那么<1 aa 2>与<2 aa 3>两行合并成1行,所有的id值和number值写到一个单元格里面,如下图所示

3.接下来就要针对虚拟表3执行Select语句了:

(1)如果执行select * 的话,那么返回的结果应该是虚拟表3,可是id和number中有的单元格里面的内容是多个值的,而关系数据库就是基于关系的,单元格中是不允许有多个值的,所以你看,执行select * 语句就报错了。

(2)我们再看name列,每个单元格只有一个数据,所以我们select name的话,就没有问题了。为什么name列每个单元格只有一个值呢,因为我们就是用name列来group by的。

(3)那么对于id和number里面的单元格有多个数据的情况怎么办呢?答案就是用聚合函数,聚合函数的作用:就是用来输入多个数据,最后输出一个数据的。如cout(id),sum(number),而每个聚合函数的输入就是每一个多数据的单元格。

(4)例如我们执行select name,sum(number) from test group by name,那么sum就对虚拟表3的number列的每个单元格进行sum操作,例如对name为aa的那一行的number列执行sum操作,即2+3,返回5;其他的行 以此类推,最后执行结果如下:

(5)group by 多个字段该怎么理解呢:如group by name,number,我们可以把name和number 看成一个整体字段,以他们整体来进行分组。如下图

(6)接下来就可以配合select和聚合函数进行操作了。如执行select name,sum(id) from test group by name,number,结果如下图:

最后总结一下:你使用了group by后,那你select的列就只能是group by的列或者对其他列进行聚合运算。

注意:新版的mysql 在group by的时候,如果你查询的字段不在分组字段里,而且你还不使用聚合函数的时候会报only_full_group_by的错

其实正规的来说,就应该给你报错,你group by的时候,查询的字段就应该是分组的字段或者对其他列进行聚合运算。

按分组字段分完组后,其他不是分组的字段,就可能出现多个值,那怎么办,那就需要用聚合函数,聚合函数的作用就是,输入多个数据,给你产出一个数据

但是有些人的sql写的不规范,他就非得想查询不是分组的字段,而且不想聚合,那怎么办?

可以通过修改mysql的sql_mode来不让它报错

那按照分组字段查询完,非分组的字段肯定会有多个值,你又不用聚合函数,那总得取一个值吧,那多个值,取哪个值呢?它默认是取多个值里的第一个。

那问题就出现在这了:虽然你通过改sql-mode让你可以在不符合规矩的情况下使用group by了

但是那些非分组,且不使用聚合函数的字段,它从多个值里取的第一个值给你返回,这个值在大多数情况下并不是你真正想要的值,可以认为这个数据是有一定的随机性的,所以返回的数据可能就会有问题。

虽然可以通过修改sql-mode来不让他报错,但是非常不建议这么改

下面是修改sql_mode的方法:

一、查询一下当前的sql_mode值
SELECT @@GLOBAL.sql_mode;  或者  SELECT @@SESSION.sql_mode;

注:下面是正常没做过修改的sql_mode的值:

ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

二、修改sql-mode

1、修改全局sql_mode

通过下面的set 把 第一项ONLY_FULL_GROUP_BY去掉

set @@global.sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';

上面的方法虽然也可以更改sql_mode,但是,一旦MySQL重启,就必须重新设置一次,这样非常的麻烦,还有其它一劳永逸的方法吗?

答案就是下面要说的第二种修改方式


2、通过修改mysql的my.cnf来永久的修改sql-mode
linux下的my.cnf一般在 /etc下
vim /etc/my.cnf
输入关键字 sql-mode 找到后,改成自己想要改的值,进行修改保存

然后重启mysql
service mysqld stop
service mysqld start
 

讲完了对group by的理解,下一篇讲一下GROUP_CONCAT的使用:大白话聊一聊mysql分组中的GROUP_CONCAT使用

如果觉得文章对你有所帮助,可以点关注,点赞

对了,铁子们,我最近开通了几个专栏,里边有更多用大白话讲的干货,全部都是我自己对一些知识点的理解,绝对干货,不拖泥带水,感兴趣的小伙伴可以去看看,专栏持续更新中,后期会持续更新更多的大白话干货

通俗易懂的大白话干货系列

带你玩转实际工作中的Jenkins系列

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

曹举的个人博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值