1. Group By 语句简介:express
Group By语句从英文的字面意义上理解就是“根据(by)必定的规则进行分组(Group)”。它的做用是经过必定的规则将一个数据集划分红若干个小的区域,而后针对若干个小区域进行数据处理。函数
P.S. 这里真是体会到了一个好的命名的力量,Group By从字面是直接去理解是很是好理解的。恩,之后在命名的环节必定要加把劲:)。话题扯远了。ui
2. Group By 的使用:spa
上面已经给出了对Group By语句的理解。基于这个理解和SQL Server 2000的联机帮助,下面对Group By语句的各类典型使用进行依次列举说明。排序
2.1 Group By [Expressions]:ci
这个恐怕是Group By语句最多见的用法了,Group By + [分组字段](能够有多个)。在执行了这个操做之后,数据集将根据分组字段的值将一个数据集划分红各个不一样的小组。好比有以下数据集,其中水果名称(FruitName)和出产国家(ProductPlace)为联合主键:it
FruitName | ProductPlace | Price |
Apple | China | $1.1 |
Apple | Japan | $2.1 |
Apple | USA | $2.5 |
Orange | China | $0.8 |
Banana | China | $3.1 |
Peach | USA | $3.0 |
若是咱们想知道每一个国家有多少种水果,那么咱们能够经过以下SQL语句来完成:io
SELECT COUNT(*) AS 水果种类, ProductPlace AS 出产国
FROM T_TEST_FRUITINFO
GROUP BY ProductPlacetable
这个SQL语句就是使用了Group By + 分组字段的方式,那么这句SQL语句就能够解释成“我按照出产国家(ProductPlace)将数据集进行分组,而后分别按照各个组来统计各自的记录数量。”很好理解对吧。这里值得注意的是结果集中有两个返回字段,一个是ProductPlace(出产国), 一个是水果种类。若是咱们这里水果种类不是用Count(*),而是相似以下写法的话:function
SELECT FruitName, ProductPlace FROM T_TEST_FRUITINFO GROUP BY ProductPlace
那么SQL在执行此语句的时候会报以下的相似错误:
选择列表中的列 ’T_TEST_FRUITINFO.FruitName’ 无效,由于该列没有包含在聚合函数或 GROUP BY 子句中。
这就是咱们须要注意的一点,若是在返回集字段中,这些字段要么就要包含在Group By语句的后面,做为分组的依据;要么就要被包含在聚合函数中。咱们能够将Group By操做想象成以下的一个过程,首先系统根据SELECT 语句获得一个结果集,如最开始的那个水果、出产国家、单价的一个详细表。而后根据分组字段,将具备相同分组字段的记录归并成了一条记录。这个时候剩下的那些不存在于Group By语句后面做为分组依据的字段就有可能出现多个值,可是目前一种分组状况只有一条记录,一个数据格是没法放入多个数值的,因此这里就须要经过必定的处理将这些多值的列转化成单值,而后将其放在对应的数据格中,那么完成这个步骤的就是聚合函数。这就是为何这些函数叫聚合函数(aggregate functions)了。
2.2 Group By All [expressions] :
Group By All + 分组字段, 这个和前面提到的Group By [Expressions]的形式多了一个关键字ALL。这个关键字只有在使用了where语句的,且where条件筛选掉了一些组的状况才能够看出效果。在SQL Server 2000的联机帮助中,对于Group By All是这样进行描述的:
若是使用 ALL 关键字,那么查询结果将包括由 GROUP BY 子句产生的全部组,即便某些组没有符合搜索条件的行。没有 ALL 关键字,包含 GROUP BY 子句的 SELECT 语句将不显示没有符合条件的行的组。
其中有这么一句话“若是使用ALL关键字,那么查询结果将包含由Group By子句产生的全部组…没有ALL关键字,那么不显示不符合条件的行组。”这句话听起来好像挺耳熟的,对了,好像和LEFT JOIN 和 RIGHT JOIN 有点像。其实这里是类比LEFT JOIN来进行理解的。仍是基于以下这样一个数据集:
FruitName | ProductPlace | Price |
Apple | China | $1.1 |
Apple | Japan | $2.1 |
Apple | USA | $2.5 |
Orange | China | $0.8 |
Banana | China | $3.1 |
Peach | USA | $3.0 |
首先咱们不使用带ALL关键字的Group By语句:
SELECT COUNT(*) AS 水果种类, ProductPlace AS 出产国
FROM T_TEST_FRUITINFO
WHERE (ProductPlace <> ’Japan’)
GROUP BY ProductPlace
那么在最后结果中因为Japan不符合where语句,因此分组结果中将不会出现Japan。
如今咱们加入ALL关键字:
SELECT COUNT(*) AS 水果种类, ProductPlace AS 出产国
FROM T_TEST_FRUITINFO
WHERE (ProductPlace <> ’Japan’)
GROUP BY ALL ProductPlace
从新运行后,咱们能够看到Japan的分组,可是对应的“水果种类”不会进行真正的统计,聚合函数会根据返回值的类型用默认值0或者NULL来代替聚合函数的返回值。
2.3 GROUP BY [Expressions] WITH CUBE | ROLLUP:
首先须要说明的是Group By All 语句是不能和CUBE 和 ROLLUP 关键字一块儿使用的。
首先先说说CUBE关键字,如下是SQL Server 2000联机帮助中的说明:
指定在结果集内不只包含由 GROUP BY 提供的正常行,还包含汇总行。在结果集内返回每一个可能的组和子组组合的 GROUP BY 汇总行。GROUP BY 汇总行在结果中显示为 NULL,但可用来表示全部值。使用 GROUPING 函数肯定结果集内的空值是不是 GROUP BY 汇总值。
结果集内的汇总行数取决于 GROUP BY 子句内包含的列数。GROUP BY 子句中的每一个操做数(列)绑定在分组 NULL 下,而且分组适用于全部其它操做数(列)。因为 CUBE 返回每一个可能的组和子组组合,所以不论指定分组列时所使用的是什么顺序,行数都相同。
咱们一般的Group By语句是按照其后所跟的全部字段进行分组,而若是加入了CUBE关键字之后,那么系统将根据全部字段进行分组的基础上,还会经过对全部这些分组字段全部可能存在的组合造成的分组条件进行分组计算。因为上面举的例子过于简单,这里就再适合了,如今咱们的数据集将换一个场景,一个表中包含人员的基本信息:员工所在的部门编号(C_EMPLINFO_DEPTID)、员工性别(C_EMPLINFO_SEX)、员工姓名(C_EMPLINFO_NAME)等。那么我如今想知道每一个部门各个性别的人数,那么咱们能够经过以下语句获得:
SELECT C_EMPLINFO_DEPTID, C_EMPLINFO_SEX, COUNT(*) AS C_EMPLINFO_TOTALSTAFFNUM
FROM T_PERSONNEL_EMPLINFO
GROUP BY C_EMPLINFO_DEPTID, C_EMPLINFO_SEX
可是若是我如今但愿知道:
1. 全部部门有多少人(这里至关于就不进行分组了,由于这里已经对员工的部门和性别没有作任何限制了,可是这的确也是一种分组条件的组合方式);
2. 每种性别有多人(这里其实是仅仅根据性别(C_EMPLINFO_SEX)进行分组);
3. 每一个部门有多少人(这里仅仅是根据部门(C_EMPLINFO_DEPTID)进行分组);那么咱们就可使用ROLLUP语句了。
SELECT C_EMPLINFO_DEPTID, C_EMPLINFO_SEX, COUNT(*) AS C_EMPLINFO_TOTALSTAFFNUM
FROM T_PERSONNEL_EMPLINFO
GROUP BY C_EMPLINFO_DEPTID, C_EMPLINFO_SEX WITH CUBE
那么这里你能够看到结果集中多出了不少行,并且结果集中的某一个字段或者多个字段、甚至所有的字段都为NULL,请仔细看一下你就会发现实际上这些记录就是完成了上面我所列举的全部统计数据的展示。使用过SQL Server 2005或者RDLC的朋友们必定对于矩阵的小计和分组功能有印象吧,是否是均可以经过这个获得答案。我想RDLC中对于分组和小计的计算就是经过Group By的CUBE和ROLLUP关键字来实现的。(我的意见,未证明)
CUBE关键字还有一个极为类似的兄弟ROLLUP, 一样咱们先从这英文入手,ROLL UP是“向上卷”的意思,若是说CUBE的组合是绝对自由的,那么ROLLUP的组合就须要有点约束了。咱们先来看看SQL Server 2000的联机中对ROLLUP关键字的定义:
指定在结果集内不只包含由 GROUP BY 提供的正常行,还包含汇总行。按层次结构顺序,从组内的最低级别到最高级别汇总组。组的层次结构取决于指定分组列时所使用的顺序。更改分组列的顺序会影响在结果集内生成的行数。
那么这个顺序是什么呢?对了就是Group By 后面字段的顺序,排在靠近Group By的分组字段的级别高,而后是依次递减。如:Group By Column1, Column2, Column3。那么分组级别从高到低的顺序是:Column1 > Column2 > Column3。仍是看咱们前面的例子,SQL语句中咱们仅仅将CUBE关键字替换成ROLLUP关键字,如:
SELECT C_EMPLINFO_DEPTID, C_EMPLINFO_SEX, COUNT(*) AS C_EMPLINFO_TOTALSTAFFNUM
FROM T_PERSONNEL_EMPLINFO
GROUP BY C_EMPLINFO_DEPTID, C_EMPLINFO_SEX WITH ROLLUP
和CUBE相比,返回的数据行数减小了很多。:),仔细看一下,除了正常的Group By语句后,数据中还包含了:
1. 部门员工数;(向上卷了一次,此次先去掉了员工性别的分组限制)
2. 全部部门员工数;(向上又卷了依次,此次去掉了员工所在部门的分组限制)。
在现实的应用中,对于报表的一些统计功能是颇有帮助的。
这里还有一个问题须要补充说明一下,若是咱们使用ROLLUP或者CUBE关键字,那么将产生一些小计的行,这些行中被剔除在分组因素以外的字段将会被设置为NULL,那么还存在一种状况,好比在做为分组依据的列表中存在可空的行,那么NULL也会被做为一个分组表示出来,因此这里咱们就不能仅仅经过NULL来判断是否是小计记录了。下面的例子展现了这里说获得的状况。仍是咱们前面提到的水果例子,如今咱们在每种商品后面增长一个“折扣列”(Discount),用于显示对应商品的折扣,这个数值是可空的,也就是能够经过NULL来表示没有对应的折扣信息。数据集以下所示:
FruitName | ProductPlace | Price | Discount |
Apple | China | $1.1 | 0.8 |
Apple | Japan | $2.1 | 0.9 |
Apple | USA | $2.5 | 1.0 |
Orange | China | $0.8 | NULL |
Banana | China | $3.1 | NULL |
Peach | USA | $3.0 | NULL |
如今咱们要统计“各类折扣对应有多少种商品,并总计商品的总数。”,那么咱们能够经过以下的SQL语句来完成:
SELECT COUNT(*) AS ProductCount, Discount
FROM T_TEST_FRUITINFO
GROUP BY Discount WITH ROLLUP
好了,运行一下,你会发现数据都正常出来了,按照如上的数据集,结果以下所示:
ProductCount | Discount |
3 | NULL |
1 | 0.8 |
1 | 0.9 |
1 | 1.0 |
6 | NULL |
好了,各类折扣的商品数量都出来了,可是在显示“没有折扣商品”和“商品小计”的时候判断上确存在问题,由于存在两条Discount为Null的记录。是哪一条呢?经过分析数据咱们知道第一条数据(3, Null)应该对应没有折扣商品的数量,而(6,Null)应该对应全部商品的数量。须要判断这两个具备不一样意义的Null就须要引入一个聚合函数Grouping。如今咱们把语句修改一下,在返回值中使用Grouping函数增长一列返回值,SQL语句以下:
SELECT COUNT(*) AS ProductCount, Discount, GROUPING(Discount) AS Expr1
FROM T_TEST_FRUITINFO
GROUP BY Discount WITH ROLLUP
这个时候,咱们再看看运行的结果:
ProductCount | Discount | Expr1 |
3 | NULL | 0 |
1 | 0.8 | 0 |
1 | 0.9 | 0 |
1 | 1.0 | 0 |
6 | NULL | 1 |
对于根据指定字段Grouping中包含的字段进行小计的记录,这里会标记为1,咱们就能够经过这个标记值将小计记录从判断那些因为ROLLUP或者CUBE关键字产生的行。Grouping(column_name)能够带一个参数,Grouping就会去判断对应的字段值的NULL是不是由ROLLUP或者CUBE产生的特殊NULL值,若是是那么就在由Grouping聚合函数产生的新列中将值设置为1。注意Grouping只会检查Column_name对应的NULL来决定是否将值设置为1,而不是彻底由此列是不是由ROLLUP或者CUBE关键字自动添加来决定的。
2.2 Group By 和 Having, Where ,Order by语句的执行顺序:
最后要说明一下的Group By, Having, Where, Order by几个语句的执行顺序。一个SQL语句每每会产生多个临时视图,那么这些关键字的执行顺序就很是重要了,由于你必须了解这个关键字是在对应视图造成前的字段进行操做仍是对造成的临时视图进行操做,这个问题在使用了别名的视图尤为重要。以上列举的关键字是按照以下顺序进行执行的:Where, Group By, Having, Order by。首先where将最原始记录中不知足条件的记录删除(因此应该在where语句中尽可能的将不符合条件的记录筛选掉,这样能够减小分组的次数),而后经过Group By关键字后面指定的分组条件将筛选获得的视图进行分组,接着系统根据Having关键字后面指定的筛选条件,将分组视图后不知足条件的记录筛选掉,而后按照Order By语句对视图进行排序,这样最终的结果就产生了。在这四个关键字中,只有在Order By语句中才可使用最终视图的列名,如:
SELECT FruitName, ProductPlace, Price, ID AS IDE, Discount
FROM T_TEST_FRUITINFO
WHERE (ProductPlace = N’china’)
ORDER BY IDE
这里只有在ORDER BY语句中才可使用IDE,其余条件语句中若是须要引用列名则只能使用ID,而不能使用IDE。