MySQL:为何查询列表中多了它,GROUP BY语句就会报错呢?

标签: 公众号文章mysql


事前准备

为了故事的顺利发展,咱们先得建一个表:sql

CREATE TABLE student_score (
  number INT(11) NOT NULL,
  name VARCHAR(30) NOT NULL,
  subject VARCHAR(30) NOT NULL,
  score TINYINT(4) DEFAULT NULL,
  PRIMARY KEY (number,subject)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
复制代码

这个student_score表是用来存储学生成绩的,咱们为这个条填充一些数据,填充后的效果就像这样:bash

mysql> SELECT * FROM student_score;
+----------+-----------+-----------------------------+-------+
| number   | name      | subject                     | score |
+----------+-----------+-----------------------------+-------+
| 20180101 | 杜子腾    | 母猪的产后护理              |    78 |
| 20180101 | 杜子腾    | 论萨达姆的战争准备          |    88 |
| 20180102 | 杜琦燕    | 母猪的产后护理              |   100 |
| 20180102 | 杜琦燕    | 论萨达姆的战争准备          |    98 |
| 20180103 | 范统      | 母猪的产后护理              |    59 |
| 20180103 | 范统      | 论萨达姆的战争准备          |    61 |
| 20180104 | 史珍香    | 母猪的产后护理              |    55 |
| 20180104 | 史珍香    | 论萨达姆的战争准备          |    46 |
+----------+-----------+-----------------------------+-------+
8 rows in set (0.00 sec)
复制代码

GROUP BY是在干什么?

咱们知道MySQL提供了一系列的汇集函数,诸如:服务器

  • COUNT:统计记录数。函数

  • MAX:查询某列的最大值。ui

  • MIN:查询某列的最小值。this

  • SUM:某列数据的累加总和。spa

  • AVG:某列数据的平均数。设计

比方说咱们想查看一下student_score表中全部人成绩的平均数就能够这么写:code

mysql> SELECT AVG(score) FROM student_score;
+------------+
| AVG(score) |
+------------+
|    73.1250 |
+------------+
1 row in set (0.00 sec)
复制代码

若是咱们只想查看《母猪的产后护理》这个科目的平均成绩,那加个WHERE子句就行了:

mysql> SELECT AVG(score) FROM student_score WHERE subject = '母猪的产后护理';
+------------+
| AVG(score) |
+------------+
|    73.0000 |
+------------+
1 row in set (0.00 sec)
复制代码

同理,咱们也能够单独查看《论萨达姆的战争准备》这门课程的平均成绩:

mysql> SELECT AVG(score) FROM student_score WHERE subject = '论萨达姆的战争准备';
+------------+
| AVG(score) |
+------------+
|    73.2500 |
+------------+
1 row in set (0.00 sec)
复制代码

这时候问题来了,若是这个student_score表中存储了20门科目的成绩信息,那咱们怎么单独的获得这20门课程的平均成绩呢?单独写20个查询语句?那要是有100门课呢?

很显然,不能傻兮兮的写一百个语句,设计MySQL的大叔给咱们提供了分组的概念。咱们能够按照某个列将表中的数据进行分组,比方说咱们如今按照subject列对表中数据进行分组,那么全部的记录就会被分红2组,如图所示:

image_1dikgr2ak1ggoo7come1opdj7i9.png-151.2kB

MySQL产生这样子的分组的语句就是GROUP BY子句,咱们只要在GROUP BY后边把须要分组的列写上就好,而后在查询列表处就能够针对每个分组来写相应的汇集函数去统计该分组,就像这样:

mysql> SELECT subject, AVG(score) FROM student_score GROUP BY subject;
+-----------------------------+------------+
| subject                     | AVG(score) |
+-----------------------------+------------+
| 母猪的产后护理              |    73.0000 |
| 论萨达姆的战争准备          |    73.2500 |
+-----------------------------+------------+
2 rows in set (0.00 sec)
复制代码

报错

能够从上边带有GROUP BY子句的查询语句中看出来,咱们只在查询列表处放了分组列subject以及对该分组中的记录调用的汇集函数AVG,那若是咱们把不是分组列的字段也放到查询列表中会出现啥状况:

mysql> SELECT subject, name, AVG(score) FROM student_score GROUP BY subject;

ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'dahaizi.student_score.name' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

mysql>
复制代码

能够看到报错了,为啥会报错呢?回想一下咱们使用GROUP BY子句的初衷,咱们只是想把记录分为若干组,而后再对各个组分别调用汇集函数去作一些统计工做。本例中的查询列表处放置了既非分组列、又非汇集函数的name列,那咱们想表达啥意思呢?从各个分组中的记录中取一个记录的name列?该取哪条记录为好呢?比方说对于'母猪的产后护理'这个分组中的记录来讲,name列的值应该取杜子腾,仍是杜琦燕,仍是范统,仍是史珍香呢?这个咱们也不知道,因此把非分组列放到查询列表中会引发争议,致使结果不肯定,因此设计MySQL的大叔才会为上述语句报错。

不过有的同窗会说,假如分组后的某个分组的某个非分组列的值都同样,那我把该非分组列加入到查询列表中也没啥问题呀。比方说按照subject列进行分组后,假如在'母猪的产后护理'的分组中各条记录的name列的值都相同,在'论萨达姆的战争准备'的分组中各条记录的name列的值也都相同,那么咱们把name列放在查询列表中也没啥问题。可能设计MySQL的大叔以为这种说法也有点儿道理,他们居然赞成在一些状况下把非分组列也放到查询列表中,这就设计到一个称之为sql_mode的系统变量,咱们先看一下在个人电脑上这个系统变量的值:

mysql> SHOW VARIABLES LIKE 'sql_mode';
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| Variable_name | Value                                                                                                                                     |
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| 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 |
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.02 sec)
复制代码

哇唔,好长的一段。不过你们没必要在乎,咱们只关心其中一个称之为ONLY_FULL_GROUP_BY的家伙。只要sql_mode的值里边有这个东东,MySQL服务器就“比较正常”(也就是不容许非分组列放到查询列表中),可是若是咱们把这个东东从sql_mode系统变量中移除(移除这个东东只要从新设置一下这个系统变量,把这个东东从值里边去除掉就好,咱们如今没必要要关心值里边儿后边那一坨东西是干吗的,照着抄下来就好):

mysql> set sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';

Query OK, 0 rows affected (0.00 sec)
复制代码

而后再执行上边那个曾经报错的语句:

mysql> SELECT subject, name, AVG(score) FROM student_score GROUP BY subject;
+-----------------------------+-----------+------------+
| subject                     | name      | AVG(score) |
+-----------------------------+-----------+------------+
| 母猪的产后护理              | 杜子腾    |    73.0000 |
| 论萨达姆的战争准备          | 杜子腾    |    73.2500 |
+-----------------------------+-----------+------------+
2 rows in set (0.00 sec)
复制代码

看,这回就不会报错了。但这是个好事儿么?我的以为不是,由于MySQL服务器也不能保证结果集中的name列的值究竟是分组中的哪条记录的。你们在平常工做中,也但愿尽可能不要用这个投机取巧的功能,没啥乱用,并且容易产生错误。

小贴士: 不一样MySQL版本中sql_mode的值可能默认包含ONLY_FULL_GROUP_BY这个家伙,也可能不包含ONLY_FULL_GROUP_BY这个家伙,也就是说不一样MySQL版本中可能默认不支持查询列表中包含非分组列,也可能默认支持查询列表中包含非分组列。

题外话

写文章挺累的,有时候你以为阅读挺流畅的,那实际上是背后无数次修改的结果。若是你以为不错请帮忙转发一下,万分感谢~ 这里是个人公众号「咱们都是小青蛙」,里边有更多技术干货,时不时扯一下犊子,欢迎关注:

相关文章
相关标签/搜索