SQL中的Group By的查询过程多列分组的查询过程是怎样的?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQL中的Group By的查询过程多列分组的查询过程是怎样的?相关的知识,希望对你有一定的参考价值。

参考技术A Group By子句\\r\\n Group By子句可以将表的行划分为不同的组。分别总结每个组,这样就可以控制想要看见的详细信息的级别。\\r\\n \\r\\n 语法:\\r\\n \\r\\n [ Group By [ ALL ] Group_By_expression[ ,...n ]\\r\\n \\r\\n [ WITH CUBE | ROLLUP ] ]\\r\\n \\r\\n 参数说明:\\r\\n \\r\\n ALL:包含所有组和结果集,甚至包含那些任何行都不满足WHERE子句指定的搜索条件的组和结果集。如果指定了ALL,将对组中不满足搜索条件的汇总列返回空值。不能用CUBE或ROLLUP运算符指定ALL。如果访问远程表的查询中有WHERE子句,则不支持Group By ALL操作。\\r\\n \\r\\n Group_By_expression:对其执行分组的表达式。Group_By_expression也称为分组列。Group_By_expression可以是列或引用列的非聚合表达式。在选择列表内定义的列的别名不能用于指定分组列。对于不包含CUBE或ROLLUP的Group By子句,Group_By_ expression的项数受查询所涉及的Group By列的大小、聚合列和聚合值的限制。该限制从8060字节的限制开始,对保存中间查询结果所需的中间级工作表有8060字节的限制。如果指定了CUBE或ROLLUP,则最多只能有10个分组表达式。\\r\\n \\r\\n CUBE:指定在结果集内不仅包含由Group By提供的正常行,还包含汇总行。在结果集内返回每个可能的组和子组组合的Group By汇总行。Group By汇总行在结果中显示为NULL,但可用来表示所有值。使用GroupING函数确定结果集内的空值是否是Group By汇总值。结果集内的汇总行数取决于Group By子句内包含的列数。Group By子句中的每个操作数(列)绑定在分组NULL下,并且分组适用于所有其他操作数(列)。由于CUBE返回每个可能的组和子组组合,因此,不论指定分组列所使用的是什么顺序,行数都相同。\\r\\n \\r\\n ROLLUP:指定在结果集内不仅包含由Group By提供的正常行,还包含汇总行。按层次结构顺序,从组内的最低级别到最高级别汇总组。组的层次结构取决于指定分组列时所使用的顺序。更改分组列的顺序会影响在结果集内生成的行数。\\r\\n \\r\\n 使用Group By子句的注意事项。\\r\\n \\r\\n (1)在SELECT子句的字段列表中,除了聚集函数外,其他所出现的字段一定要在Group By子句中有定义才行。例如“Group By A,B”,那么“SELECT SUM(A),C”就有问题,因为C不在Group By中,但是SUM(A)是可以的。\\r\\n \\r\\n (2)SELECT子句的字段列表中不一定要有聚集函数,但至少要用到Group By子句列表中的一个项目。例如“Group By A,B,C”,则“SELECT A”是可以的。\\r\\n \\r\\n (3)在SQL Server中text、ntext和image数据类型的字段不能作为Group By子句的分组依据。\\r\\n \\r\\n (4)Group By子句不能使用字段别名。\\r\\n 1.按单列进行分组\\r\\n \\r\\n Group By子句可以基于指定某一列的值将数据集合划分为多个分组,同一组内所有记录在分组属性上具有相同值。\\r\\n \\r\\n 示例:\\r\\n \\r\\n 把“student”表按照“性别”这个单列进行分组。在查询分析器中输入的SQL语句如下:\\r\\n \\r\\n use student\\r\\n \\r\\n select 性别\\r\\n \\r\\n from student\\r\\n \\r\\n Group By 性别\\r\\n \\r\\n 但仍然要强调SELECT子句必须与Group By后的子句或者是分组函数列相一致。\\r\\n \\r\\n 例如,由于下列查询中“姓名”列既不包含在Group By子句中,也不包含在分组函数中,所以是错误的。错误的SQL语句如下:\\r\\n \\r\\n use student select 姓名,性别 from student Group By 性别\\r\\n \\r\\n 例如,在“grade”表中,按“学期”分组查询。SQL语句如下:\\r\\n \\r\\n use studnet select 学期 from grade Group By 学期\\r\\n \\r\\n 2.按多列进行分组\\r\\n \\r\\n Group By子句可以基于指定多列的值将数据集合划分为多个分组。\\r\\n \\r\\n 示例:\\r\\n \\r\\n 在“student”表中,按照“性别”和“年龄”列进行分组。在查询分析中输入的SQL语句如下:\\r\\n \\r\\n use student\\r\\n \\r\\n select 性别,年龄\\r\\n \\r\\n from student\\r\\n \\r\\n Group By 性别,年龄\\r\\n \\r\\n 在“student”表中,首先按照“性别”分组,然后再按照“年龄”分组。\\r\\n \\r\\n 再举一个例子,例如,在“grade”表中,按照“学号”和“课程代号”列进行分组。SQL语句如下:\\r\\n \\r\\n use student\\r\\n \\r\\n select 学号,课程代号 from grade Group By 学号,课程代号\\r\\n \\r\\n 按多列进行分组时有NULL组的是如何处理的。当表按多列进行分组时有NULL组,这时NULL被作为一个特定值处理,就像其他任何值一样。也就是说,如果在某个分组列中存在两个NULL,则按它们有相同的值那样处理,并将它们放在相同的组中。\\r\\n \\r\\n 示例:\\r\\n \\r\\n 在“grade”表中,按“学期”和“课程代号”列进行分组。在查询分析器中输入的SQL语句如下:\\r\\n \\r\\n use student\\r\\n \\r\\n select 学期,课程代号\\r\\n \\r\\n from grade\\r\\n \\r\\n Group By 学期,课程代号\\r\\n \\r\\n 3.与聚集函数一起使用\\r\\n \\r\\n Group By子句是经常与聚集函数一起使用。如果SELECT子句中包含聚集函数,则计算每组的汇总值,当用户指定Group By时,选择列表中任一非聚集表达式内的所有列都应包含在Group By列表中,或者Group By表达式必须与选择列表表达式完全匹配。\\r\\n \\r\\n 示例:\\r\\n \\r\\n 在“student”表中,分别求男女生的平均年龄。在查询分析器中输入的SQL语句如下:\\r\\n \\r\\n use student\\r\\n \\r\\n select 性别,avg(年龄) as 平均年龄\\r\\n \\r\\n from student\\r\\n \\r\\n Group By 性别\\r\\n \\r\\n 例如,在“student”表中,分别求有多少个男生和女生。SQL语句如下:\\r\\n \\r\\n use student\\r\\n \\r\\n select 性别,count(性别) as 人数 from student Group By 性别\\r\\n \\r\\n 说明:关于聚合函数的详细讲解可参阅9.2.1节。\\r\\n \\r\\n 4.与HAVING子句一起使用\\r\\n \\r\\n HAVING子句对Group By子句选择出来的结果进行再次筛选,最后输出符合HAVING子句中条件的记录。HAVING子句的语法与WHERE子句的语法相类似,惟一不同的是HAVING子句中可以包含聚合函数。\\r\\n \\r\\n 语法:\\r\\n \\r\\n [HAVING ]\\r\\n \\r\\n 参数说明:\\r\\n \\r\\n :指定组或聚合应满足的搜索条件。当HAVING与Group By ALL一起使用时,HAVING 子句替代ALL。\\r\\n \\r\\n 示例:\\r\\n \\r\\n 在“student”表中,按“性别”分组求平均年龄,并且查询其平均年龄大于21的学生信息。在查询分析器中输入的SQL语句如下:\\r\\n \\r\\n use student\\r\\n \\r\\n select avg(年龄), 性别\\r\\n \\r\\n from student\\r\\n \\r\\n Group By 性别\\r\\n \\r\\n having avg(年龄)>21\\r\\n \\r\\n 在“grade”表中,按“学期”分组求平均成绩,并且查询“平均成绩”大于93的课程信息。在查询分析器中输入的SQL语句如下:\\r\\n +1 已赞过

分组查询时,select的字段是否一定要都在group by中?

    分组查询关键字group by通常和集合函数(MAX、MIN、COUNT、SUM、AVG)一起使用,它可以对一列或者多列结果集进行分组。例如要统计超市水果的种类,需要用count函数,要统计哪个水果价格最高,要用MAX()函数。

    一般情况下,我们在使用group by的时候,select中的列都要出现在group by中,比如select id,name,age from tuser group by id,name,age,那么我们是不是都要严格按照这种模式来写sql呢?下面我们来一起探索下。

数据准备

创建一张学生表

CREATE TABLE `student1` (  `id` int(11) NOT NULL COMMENT '学号',  `name` varchar(60) NOT NULL COMMENT '姓名',  `birth` date NOT NULL COMMENT '出生日期',  `sex` varchar(1) DEFAULT NULL,  `age` int(11) NOT NULL,  `score` int(11) NOT NULL,  PRIMARY KEY (`id`))

插入数据

insert into student values(1,'Tom','1998-10-01','男',23,96),(2,'Jim','1997-07-04','男',24,95),(3,'Lily','1999-11-12','女',21,99),(4,'Lilei','1996-09-21','男',25,90),(5,'Lucy','1999-12-02','女',21,93),(6,'Jack','1988-04-27','男',32,89),(7,'Liam','1991-09-08',' 男',28,100);

数据展示

mysql> select * from student;+----+-------+------------+------+-----+-------+| id | name  | birth      | sex  | age | score |+----+-------+------------+------+-----+-------+|  1 | Tom   | 1998-10-01 | 男   |  23 |    96 ||  2 | Jim   | 1997-07-04 | 男   |  24 |    95 ||  3 | Lily  | 1999-11-12 | 女   |  21 |    99 ||  4 | Lilei | 1996-09-21 | 男   |  25 |    90 ||  5 | Lucy  | 1999-12-02 | 女   |  21 |    93 ||  6 | Jack  | 1988-04-27 | 男   |  32 |    89 ||  7 | Liam  | 1991-09-08 | 男   |  28 |   100 |+----+-------+------------+------+-----+-------+7 rows in set (0.00 sec)

测试验证

1. select中的列都出现在group by中,通过下面的结果可以看出是可以正常执行的。

mysql> select id,name,score from student where score >95  group by id,name,score;
+----+------+-------+
| id | name | score |
+----+------+-------+
|  1 | Tom  |    96 |
|  3 | Lily |    99 |
|  7 | Liam |   100 |
+----+------+-------+
3 rows in set (0.01 sec)

2. group by中只保留score或者name

mysql> select id,name,score from student where score >95  group by score;
ERROR 1055 (42000): Expression #1 of 
SELECT list is not in GROUP BY clause 
and contains nonaggregated column 
'test.student.id' which is not functionally 
dependent on columns in GROUP BY clause; 
this is incompatible with sql_mode=only_full_group_by
mysql> select id,name,score from student where score >95  group by name;
ERROR 1055 (42000): Expression #1 of 
SELECT list is not in GROUP BY clause 
and contains nonaggregated column 
'test.student.id' which is not functionally 
dependent on columnsin GROUP BY clause; 
this is incompatible with sql_mode=only_full_group_by

3. group by中只保留id

mysql> select id,name,score from student where score >95  group by id;
+----+------+-------+
| id | name | score |
+----+------+-------+
|  1 | Tom  |    96 |
|  3 | Lily |    99 |
|  7 | Liam |   100 |
+----+------+-------+
3 rows in set (0.00 sec)

通过这个实验可以看出group by中只保留id是可以正常执行的,为什么?id字段有什么特殊性呢?

通过表结构可以看出id字段是主键,查询官方文档,有针对主键列的解释。

SELECT name, address, MAX(age) FROM t GROUP BY name;
The query is valid if name is a primary key of t or is a unique NOT NULL column. In such cases,MySQL recognizes that the selected column is functionally dependent on a grouping column. Forexample, if name is a primary key, its value determines the value of address because each group has only one value of the primary key and thus only one row. As a result, there is no randomness in the choice of address value in a group and no need to reject the query.

The query is invalid if name is not a primary key of t or a unique NOT NULL column.

大致的意思是:如果name列是主键或者是唯一的非空列,name上面的查询是有效的。这种情况下,MySQL能够识别出select中的列依赖于group by中的列。比如说,如果name是主键,它的值就决定了address的值,因为每个组只有一个主键值,分组中的每一行都具有唯一性,因此也不需要拒绝这个查询。

4. 验证唯一非空索引

增加name字段的唯一性约束

alter table student add unique(name);mysql> select id,name,score from student where score >95  group by name;+----+------+-------+| id | name | score |+----+------+-------+|  7 | Liam |   100 ||  3 | Lily |    99 ||  1 | Tom  |    96 |+----+------+-------+3 rows in set (0.00 sec)

通过上面的例子也验证了,对于有唯一性约束的字段,也可以不用在group by中把select中的字段全部列出来。不过针对主键或者唯一性字段进行分组查询意义并不是很大,因为他们的每一行都是唯一的。

ONLY_FULL_GROUP_BY

    我们在上面提到select中的列都出现在group by中,其实在MySQL5.7.5之前是没有此类限制的,5.7.5版本在sql_mode中增加了ONLY_FULL_GROUP_BY参数,用来开启或者关闭针对group by的限制。下面我们在分别开启和关闭ONLY_FULL_GROUP_BY限制的情况下分别进行验证。

1. 我们先查询下sql_mode

mysql> select @@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 |
+-------------------------------------------------------------------------------------------------------------------------------------------++
1 row in set (0.00 sec)

2. sql_mode动态去除ONLY_FULL_GROUP_BY限制

mysql> SET @@sql_mode = sys.list_drop(@@sql_mode, 'ONLY_FULL_GROUP_BY');Query OK, 0 rows affected (0.05 sec)

再次执行分组查询

mysql> select id,name,score from student where score >95  group by score;+----+------+-------+| id | name | score |+----+------+-------+|  1 | Tom  |    96 ||  3 | Lily |    99 ||  7 | Liam |   100 |+----+------+-------+3 rows in set (0.00 sec)

3. sql_mode动态增加ONLY_FULL_GROUP_BY限制

SET @@sql_mode = sys.list_add(@@sql_mode, 'ONLY_FULL_GROUP_BY');

再次执行分组查询

mysql> select id,name,score from student where score >95  group by score;
ERROR 1055 (42000): Expression #1 of 
SELECT list is not in GROUP BY clause 
and contains nonaggregated column 
'test.student.id' which is not functionally 
dependent on columns in GROUP BY clause; 
this is incompatible with sql_mode=only_full_group_by。

以上是关于SQL中的Group By的查询过程多列分组的查询过程是怎样的?的主要内容,如果未能解决你的问题,请参考以下文章

使用SQL语言了解Django ORM中的分组(group by)和聚合(aggregation)查询

在SQL中分组查询 Group by 的存在条件是啥

SQL group by分组查询

group by

连接查询和分组查询

分组查询时,select的字段是否一定要都在group by中?