深入理解sql分组查询(group by)

理解group by语义

个人认为sql中的group by和join是两大难点,因为它们转换了原来的表结构,group把表按某些字段统计缩小,join则使用笛卡尔积将多个表连接展开。

咱们回到group by,顾名思义group即为分组,即将原来的一整块数据分成几小块。分组是聚合的前提,聚合是在每个分组内进行一些统计,如在分组内的最大值,最小值,平均值,个数等。

未分组时查询返回的行直接与数据库表中的行对应,分组后分为多少组这个查询就会返回多少条记录,返回的记录中只能包含分组字段和在这个分组上的聚合操作的值(MySQL是例外见后面)

一般形式为:语法为:select t.sex, count(t.id) as cnt group by sex having cnt>2

查询中有三种字段:

查询字段:在select中出现的表中的字段

聚合字段:对表中的字段施加了聚合函数后形成的字段

分组字段:group by后的字段

无分组字段时

sql允许无分组字段即无group by直接在select用各聚合函数,这时表示整块数据为一个分组(单分组),聚合操作则是在整块全局上进行聚合,表示为本查询中所有记录的最大值,最小值,平均值,个数等

如果没有分组字段的聚合,则select后的字段只能全部都是带聚合的,不能直接是字段名否则Orcle会报错:ORA-00937: not a single-group group function(MySQL是个例外后面详述)

MySQL的坑

MySQL有一个让人不解的地方:在聚合查询中select可以出现非分组字段,语义上group by后的结果已经进行了分组变换,此时的数据均应对应于分组而不再是原表记录,在一个分组语义的记录里加入一个表的字段,语义上不太严谨,实际上MySQL会将非分组字段随便取一个值,不得不说算一个坑

比如下面的查询Oracle报错而MySQL不报错

select id, count(t.id) from

(

select 1 as id, 2 as age, ‘F’ as sex from dual union

select 2 as id, 2 as age, ‘M’ as sex from dual union

select 3 as id, 3 as age, ‘F’ as sex from dual union

select 4 as id, 4 as age, ‘M’ as sex from dual 

)t

在MySQL中返回

1
4

该查询是单分组查询,4是总的记录条数没有问题,此时1是什么意思呢?它和计数有什么关系呢?

对null的处理

count(*)统计包含null在内的行数,count后面跟上字段名则表示统计这个字段非null的行数

聚合函数后面可以跟一个复合表达式,如多个字段相加,复合时如果其中一个字段为null则整个表达式为null,比如统计两个字段都非null的行数,可以count(f1+f2)

count,avg,sum后面跟上字段名时如果为null值将会忽略,比如计算平均值时null的那一行并不会记入总行数

如果带有group by则select后的字段要么是分组字段要么是聚合字段(MySQL仍然可以出现非分组字段)

关于having

与where不同的是having在group后进行进行过滤,where在group前进行过滤其写在group关键字前,having后过滤的字段可以是分组字段或聚合字段如:

select sex, count(*) as cnt having sex=’F’

select sex, count(*) as cnt having cnt>1

小技巧

有时需要测试sql语法但又觉得创建表麻烦,可以使用创建一个临时表,如下:

select id, count(t.id) from

(

select 1 as id, 2 as age, ‘F’ as sex from dual union

select 2 as id, 2 as age, ‘M’ as sex from dual union

select 3 as id, 3 as age, ‘F’ as sex from dual union

select 4 as id, 4 as age, ‘M’ as sex from dual 

)t

    原文作者:mysql
    原文地址: https://blog.csdn.net/wry2008wry/article/details/80479532
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞