SQL Server温故系列(5):SQL 查询之分组查询 GROUP BY
1、GROUP BY 与聚合函数
GROUP BY 是一种能将查询结果划分为多个行组的查询语句的子句,其目的通常是为了在每个组上执行一个或多个聚合运算,所以 GROUP BY 通常会与聚合函数一块儿出现在查询语句中。
GROUP BY 的标准分组方式是按所有分组字段的值依次来分组。假如字段 A 的值有 3 种,字段 B 的值有 2 种;如果是GROUP BY A
,那么就会被分为 3 组;而如果是GROUP BY A,B
,那么就会先被 A 分为 3 组,然后这 3 组又会被 B 再各自分为 2 组,最终会被分为 3×2 等于 6 组。
显然,GROUP BY B,A
最终也会被分为 6 组,换而言之,标准分组时的字段的顺序不会对分组结果产生影响。但分组字段的顺序会影响查询结果的排序,如果想要改变结果集的排序,可以通过 ORDER BY 子句来实现。
示例一、查询统计学生 1、2、3 的第 1 次考试成绩,且按各科总分降序排列:
SELECT t.StudentId,COUNT(1) 科目数,
SUM(t.Scores) 总分,MAX(t.Scores) 最高分,MIN(t.Scores) 最低分,AVG(t.Scores) 平均分
FROM T_ExamResults t
WHERE t.Counts = 1 AND t.StudentId IN(1,2,3)
GROUP BY t.StudentId
ORDER BY 总分 DESC;
示例二、查询统计学生 1、2、3 的第 1 次考试成绩,且按班级名称和学生名称来升序排列:
SELECT t1.Code,t1.Name,t3.Name,COUNT(1) 科目数,
SUM(t2.Scores) 总分,MAX(t2.Scores) 最高分,MIN(t2.Scores) 最低分,AVG(t2.Scores) 平均分
FROM T_Students t1
JOIN T_ExamResults t2 ON t1.Id = t2.StudentId AND t2.Counts = 1
JOIN T_Classes t3 ON t1.ClassId = t3.Id
WHERE t1.Id IN(1,2,3)
GROUP BY t1.Code,t1.Name,t3.Name
ORDER BY t3.Name,t1.Name DESC;
注意:在含有 GROUP BY 子句的查询语句中,每组只会返回一行数据,且查询选择列表中的列只能是 GROUP BY 中的字段或聚合函数表达式。
2、GROUP BY 与 HAVING
HAVING 子句的作用有点类似于 WHERE 子句,说到底它们都是过滤数据用的,但不同的是,WHERE 子句过滤的最小单位是数据行,而 HAVING 子句过滤的最小单位是行组。相较于 WHERE 子句,HAVING 子句最大的优势就是支持聚合函数。
HAVING 子句只能在查询语句中使用,且通常与 GROUP BY 子句一起使用。如果查询语句中没有 GROUP BY 子句,那么就会有隐式的单一行组,但这通常是没有意义的。例如要查询统计平均分达到 80 分的学生第 1 次考试成绩,且按总分倒序排列,示例如下:
SELECT t.StudentId,COUNT(1) 科目数,
SUM(t.Scores) 总分,MAX(t.Scores) 最高分,MIN(t.Scores) 最低分,AVG(t.Scores) 平均分
FROM T_ExamResults t
WHERE t.Counts = 1
GROUP BY t.StudentId
HAVING AVG(t.Scores) >= 80
ORDER BY SUM(t.Scores) DESC;
3、GROUP BY 扩展分组
在实际的开发工作中,尤其是开发数据报表,往往需要统计多维度的小计和合计。大多数情况下用 UNION 也能达到类似效果,但实现起来比较繁琐,灵活性较差,性能往往也比较低。针对这类需求,SQL Server 提供了几个实用的扩展分组,以便能更好的实现这些需求。
3.1、GROUP BY ROLLUP
ROLLUP 是对 GROUP BY 子句的一种扩展,它允许计算标准分组及部分维度的小计及合计。ROLLUP 的计算结果与分组字段的顺序有关,因为它的分组过程具有方向性,先计算标准分组,然后从右到左递减计算更高一级的小计,直到所有字段被计算完,最后计算合计。
对于GROUP BY ROLLUP(a,b,c)
,结果具有 (a,b,c)、(a,b,NULL)、(a,NULL,NULL)、(NULL,NULL,NULL) 唯一值的组。换而言之,GROUP BY ROLLUP(a,b,c)
的结果集就等价于GROUP BY a,b,c
的结果集,加上GROUP BY a,b
的结果集,再加上GROUP BY a
的结果集,最后加上不带GROUP BY
的总计结果集。
示例一、查询统计 1、2、3 班的学生个数及年龄:
WITH t AS(
SELECT t.Code,t.Name,DATEDIFF(YEAR,t.Birthday,GETDATE()) Age,t.ClassId
FROM T_Students t
WHERE t.ClassId IN(1,2,3)
)
SELECT t.ClassId,COUNT(1) 学生个数,MAX(t.Age) 最大年龄,MIN(t.Age) 最小年龄
FROM t
GROUP BY ROLLUP(t.ClassId);
查询结果如下:
ClassId 学生个数 最大年龄 最小年龄
----------- ----------- ----------- -----------
1 10 20 15
2 9 20 16
3 9 21 15
NULL 28 21 15
示例二、查询统计 1、2 班的学生个数及年龄:
WITH t AS(
SELECT t.Code,t.Name,DATEDIFF(YEAR,t.Birthday,GETDATE()) Age,t.ClassId,t.Gender
FROM T_Students t
WHERE t.ClassId IN(1,2)
)
SELECT t.ClassId,t.Gender,COUNT(1) 学生个数,MAX(t.Age) 最大年龄,MIN(t.Age) 最小年龄
FROM t
GROUP BY ROLLUP(t.ClassId,t.Gender);
查询结果如下:
ClassId Gender 学生个数 最大年龄 最小年龄
----------- ----------- ----------- ----------- -----------
1 0 6 19 15
1 1 4 20 18
1 NULL 10 20 15
2 0 4 20 17
2 1 5 20 16
2 NULL 9 20 16
NULL NULL 19 20 15
示例三、查询统计 1、2 班的学生个数及年龄:
WITH t AS(
SELECT t.Code,t.Name,DATEDIFF(YEAR,t.Birthday,GETDATE()) Age,t.ClassId,t.Gender
FROM T_Students t
WHERE t.ClassId IN(1,2)
)
SELECT t.ClassId,t.Gender,COUNT(1) 学生个数,MAX(t.Age) 最大年龄,MIN(t.Age) 最小年龄
FROM t
GROUP BY t.ClassId,ROLLUP(t.Gender);
查询结果如下:
ClassId Gender 学生个数 最大年龄 最小年龄
----------- ----------- ----------- ----------- -----------
1 0 6 19 15
1 1 4 20 18
1 NULL 10 20 15
2 0 4 20 17
2 1 5 20 16
2 NULL 9 20 16
3.2、GROUP BY CUBE
CUBE 是对 GROUP BY 子句的一种扩展,它允许计算标准分组及所有维度的小计及合计。CUBE 会对所有可能的分组进行统计,从而生成交叉报表。CUBE 比 ROLLUP 的分组更多,完全包含了 ROLLUP 的统计结果,且计算结果与分组字段的顺序无关,但如果字段顺序不同,默认的结果集排序会有不同。
对于GROUP BY CUBE(a,b)
,结果具有 (a,b)、(a,NULL)、(NULL,b)、(NULL,NULL) 唯一值的组。换而言之,GROUP BY CUBE(a,b)
的结果集就等价于GROUP BY a,b
的结果集,加上GROUP BY a
的结果集,再加上GROUP BY b
的结果集,最后加上不带GROUP BY
的总计结果集。
示例一、查询统计 1、2、3 班的学生个数及年龄:
WITH t AS(
SELECT t.Code,t.Name,DATEDIFF(YEAR,t.Birthday,GETDATE()) Age,t.ClassId
FROM T_Students t
WHERE t.ClassId IN(1,2,3)
)
SELECT t.ClassId,COUNT(1) 学生个数,MAX(t.Age) 最大年龄,MIN(t.Age) 最小年龄
FROM t
GROUP BY CUBE(t.ClassId);
查询结果如下:
ClassId 学生个数 最大年龄 最小年龄
----------- ----------- ----------- -----------
1 10 20 15
2 9 20 16
3 9 21 15
NULL 28 21 15
示例二、查询统计 1、2 班的学生个数及年龄:
WITH t AS(
SELECT t.Code,t.Name,DATEDIFF(YEAR,t.Birthday,GETDATE()) Age,t.ClassId,t.Gender
FROM T_Students t
WHERE t.ClassId IN(1,2)
)
SELECT t.ClassId,t.Gender,COUNT(1) 学生个数,MAX(t.Age) 最大年龄,MIN(t.Age) 最小年龄
FROM t
GROUP BY CUBE(t.ClassId,t.Gender);
查询结果如下:
ClassId Gender 学生个数 最大年龄 最小年龄
----------- ----------- ----------- ----------- -----------
1 0 6 19 15
2 0 4 20 17
NULL 0 10 20 15
1 1 4 20 18
2 1 5 20 16
NULL 1 9 20 16
NULL NULL 19 20 15
1 NULL 10 20 15
2 NULL 9 20 16
示例三、查询统计 1、2 班的学生个数及年龄:
WITH t AS(
SELECT t.Code,t.Name,DATEDIFF(YEAR,t.Birthday,GETDATE()) Age,t.ClassId,t.Gender
FROM T_Students t
WHERE t.ClassId IN(1,2)
)
SELECT t.ClassId,t.Gender,COUNT(1) 学生个数,MAX(t.Age) 最大年龄,MIN(t.Age) 最小年龄
FROM t
GROUP BY t.ClassId,CUBE(t.Gender);
查询结果如下:
ClassId Gender 学生个数 最大年龄 最小年龄
----------- ----------- ----------- ----------- -----------
1 0 6 19 15
1 1 4 20 18
1 NULL 10 20 15
2 0 4 20 17
2 1 5 20 16
2 NULL 9 20 16
3.3、GROUP BY GROUPING SETS
GROUPING SETS 是对 GROUP BY 子句的一种扩展,它允许一次计算多个标准分组的小计。GROUPING SETS 的功能相当于将多个 GROUP BY 子句组合到一个 GROUP BY 子句中,类似于用 UNION ALL 合并多个 GROUP BY 的结果集,所以它的计算结果与排序字段的顺序无关,而且不会合并重复组。
例如GROUP BY GROUPING SETS(ROLLUP(A))
和GROUP BY ROLLUP(A)
的结果集相同,GROUP BY GROUPING SETS(A,B)
和GROUP BY A
加GROUP BY B
的结果集相同。示例如下:
WITH t AS(
SELECT t.Code,t.Name,DATEDIFF(YEAR,t.Birthday,GETDATE()) Age,t.ClassId,t.Gender
FROM T_Students t
WHERE t.ClassId IN(1,2)
)
SELECT t.ClassId,COUNT(1) 学生个数,MAX(t.Age) 最大年龄,MIN(t.Age) 最小年龄
FROM t
GROUP BY GROUPING SETS(ROLLUP(t.ClassId),CUBE(t.ClassId));
查询结果如下:
ClassId 学生个数 最大年龄 最小年龄
----------- ----------- ----------- -----------
1 10 20 15
2 9 20 16
NULL 19 20 15
1 10 20 15
2 9 20 16
NULL 19 20 15
GROUPING SETS 中还支持GROUP BY (),用于指定生成总计的空组。例如要查询统计浙江地区各级别行政区个数,及总计个数,示例如下:
SELECT t.Level 级别,COUNT(1) 个数
FROM T_Districts t
WHERE SUBSTRING(t.Code,1,2) = '33'
GROUP BY GROUPING SETS(t.Level,());
查询结果如下:
级别 个数
----------- -----------
1 1
2 6
3 10
NULL 17
4、GROUP BY 扩展函数
4.1、GROUPING 函数
GROUPING 函数用于指示当前行是否为聚合行,如果它返回 1 则表示聚合,相反,返回 0 则表示未聚合。仅当指定了 GROUP BY 时,GROUPING 才能在 SELECT 子句、HAVING 或 ORDER BY 子句中使用。
通常将一个分组字段作为该函数的参数,然后通过判断它的返回值来区分聚集行与常规行,从而进一步对结果集美化或过滤。示例如下:
SELECT GROUPING(t.Level) 标志,t.Level 级别,COUNT(1) 个数
FROM T_Districts t
WHERE SUBSTRING(t.Code,1,2) = '33'
GROUP BY GROUPING SETS(t.Level,());
查询结果如下:
标志 级别 个数
---- ----------- -----------
0 1 1
0 2 6
0 3 10
1 NULL 17
4.2、GROUPING_ID 函数
GROUPING 函数用于计算分组级别,它将返回与行相关联的 GROUPING 位向量对应的数值。GROUPING\_ID 按从左到右的顺序计算,如果是分组字段,则为 0,如果是小计或合计则为 1,然后按字段的顺序将计算结果组成二进制序列(位向量),最后将位向量转化为十进制数。仅当指定了 GROUP BY 时,GROUPING 才能在 SELECT 子句、HAVING 或 ORDER BY 子句中使用。
GROUPING_ID 函数在功能上等效于多个 GROUPING 函数,当查询结果有多个聚合级别时,使用该函数会更容易表达行过滤条件。示例如下:
SELECT t.CourseId,t.StudentId,t.Counts,MAX(t.Scores) Scores,
GROUPING_ID(t.CourseId) gc,GROUPING_ID(t.StudentId) gs,GROUPING_ID(t.Counts) gt,
GROUPING_ID(t.CourseId,t.StudentId) gcs,
GROUPING_ID(t.StudentId,t.Counts) gst,
GROUPING_ID(t.CourseId,t.Counts) gct,
GROUPING_ID(t.CourseId,t.StudentId,t.Counts) gcst
FROM T_ExamResults t
WHERE t.StudentId = 1 AND t.Counts = 2
GROUP BY ROLLUP(t.CourseId,t.StudentId,t.Counts);
查询结果如下:
CourseId StudentId Counts Scores gc gs gt gcs gst gct gcst
---------- ---------- --------- --------- ------- ------- ------- ------- ------- ------- -------
1 1 2 63.0 0 0 0 0 0 0 0
1 1 NULL 63.0 0 0 1 0 1 1 1
1 NULL NULL 63.0 0 1 1 1 3 1 3
2 1 2 98.0 0 0 0 0 0 0 0
2 1 NULL 98.0 0 0 1 0 1 1 1
2 NULL NULL 98.0 0 1 1 1 3 1 3
3 1 2 73.0 0 0 0 0 0 0 0
3 1 NULL 73.0 0 0 1 0 1 1 1
3 NULL NULL 73.0 0 1 1 1 3 1 3
NULL NULL NULL 98.0 1 1 1 3 3 3 7
5、本文小结
本文主要讲述了 SQL Server 中分组查询的相关知识点,包括 GROUP BY 与聚合函数、HAVING 联合使用及 GROUP BY 的标准分组、扩展分组、扩展函数等。
另外,不知道会不会有读者感到疑惑,为什么扩展分组返回的结果集中经常会出现 NULL 值?其实这是 NULL 的一个特殊应用,它在 ROLLUP、CUBE 或 GROUPING SETS 操作的结果集内作为字段的占位符,表示全体(数据)。
本文参考链接:
- 1、SQL Server 2016 GROUP BY
- 2、SQL Server 2016 HAVING
- 3、SQL Server 2016 GROUPING
- 4、SQL Server 2016 GROUPING_ID
去导航目录篇下载创建本系列博文通用库表及数据的 SQL 语句
本文链接:http://www.cnblogs.com/hanzongze/p/tsql-groupby.html
版权声明:本文为博客园博主 韩宗泽 原创,作者保留署名权!欢迎通过转载、演绎或其它传播方式来使用本文,但必须在明显位置给出作者署名和本文链接!个人博客,能力有限,若有不当之处,敬请批评指正,谢谢!
SQL Server温故系列(5):SQL 查询之分组查询 GROUP BY的更多相关文章
- SQL Server温故系列(0):导航目录
创建本系列博文通用库表及数据的 SQL 语句:下载 SQL Server温故系列(0):导航目录 SQL Server温故系列(1):SQL 数据操作 CRUD 之增删改合 SQL Server温故系 ...
- SQL Server温故系列(2):SQL 数据操作 CRUD 之简单查询
1.查询语句 SELECT 1.1.查询语句的 SELECT 子句 1.2.查询语句的 FROM 子句 1.2.1.内连接查询 INNER JOIN 1.2.2.外连接查询 OUTER JOIN 1. ...
- SQL Server温故系列(3):SQL 子查询 & 公用表表达式 CTE
1.子查询 Subqueries 1.1.单行子查询 1.2.多行子查询 1.3.相关子查询 1.4.嵌套子查询 1.5.子查询小结及性能问题 2.公用表表达式 CTE 2.1.普通公用表表达式 2. ...
- SQL Server温故系列(1):SQL 数据操作 CRUD 之增删改合
1.插入语句 INSERT INTO 1.1.用 INSERT 插入单行数据 1.2.用 INSERT 插入多行数据 1.3.用 INSERT 插入子查询结果行 1.4.INSERT 小结及特殊字段插 ...
- SQL Server温故系列(4):SQL 查询之集合运算 & 聚合函数
1.集合运算 1.1.并集运算 UNION 1.2.差集运算 EXCEPT 1.3.交集运算 INTERSECT 1.4.集合运算小结 2.聚合函数 2.1.求行数函数 COUNT 2.2.求和函数 ...
- SQL Server Reporting Service(SSRS) 第二篇 SSRS数据分组Parent Group
SQL Server Reporting Service(SSRS) 第一篇 我的第一个SSRS例子默认使用Table进行简单的数据显示,有时为了进行更加直观的数据显示,我们需要按照某个字段对列表进行 ...
- SQL Server调优系列基础篇(子查询运算总结)
前言 前面我们的几篇文章介绍了一系列关于运算符的介绍,以及各个运算符的优化方式和技巧.其中涵盖:查看执行计划的方式.几种数据集常用的连接方式.联合运算符方式.并行运算符等一系列的我们常见的运算符.有兴 ...
- SQL Server调优系列进阶篇(查询语句运行几个指标值监测)
前言 上一篇我们分析了查询优化器的工作方式,其中包括:查询优化器的详细运行步骤.筛选条件分析.索引项优化等信息. 本篇我们分析在我们运行的过程中几个关键指标值的检测. 通过这些指标值来分析语句的运行问 ...
- SQL Server调优系列玩转篇(如何利用查询提示(Hint)引导语句运行)
前言 前面几篇我们分析了关于SQL Server关于性能调优的一系列内容,我把它分为两个模块. 第一个模块注重基础内容的掌握,共分7篇文章完成,内容涵盖一系列基础运算算法,详细分析了如何查看执行计划. ...
随机推荐
- OpenStack Summit Paris 会议记录 - 11-05-2014
Ops/Design Summit - 2014-11-05 Record 1. Keystone Operators, Deployers, and DevOps 1. Icehouse中,SAML ...
- Raw-OS备用事件源代码分析
作为分析的内核版本2014-04-15,基于1.05正式版,blogs我们会跟上的内核开发进度的最新版本,如果出现源代码的目光"???"的话.没有深究的部分是理解. Raw-OS官 ...
- .NET 上传并解析CSV文件存库
1.前端: 放置浏览按钮 <div class="row inner_table text-center"> <input id="fileId&quo ...
- WPF 自定义图片剪切器 - 头像剪切(扩展与完善、实时截图)
原文:WPF 自定义图片剪切器 - 头像剪切(扩展与完善.实时截图) 一.说明:上一次写的"WPF 自定义图片剪切器 - 头像剪切.你懂得"存在明显的缺陷,由于篇幅较长.重新写了一 ...
- redis SERVER INSTALL WINDOWS SERVICE
以管理 员身份 运行 CMD 命令,进入redis所在目录,并运行下 脚本redis-server --service-install redis.windows-service.conf --log ...
- Unpaired/Partially/Unsupervised Image Captioning
这篇涉及到以下三篇论文: Unpaired Image Captioning by Language Pivoting (ECCV 2018) Show, Tell and Discriminate: ...
- GIS基础软件及操作(四)
原文 GIS基础软件及操作(四) 练习四.空间数据处理 1.空间数据处理(融合.合并.剪切.交叉.合并)2.设置地图投影及投影变换 空间数据处理 数据:云南县界.shp; Clip.shp西双版纳森林 ...
- intellij开发安卓与genymotion配合
原文:intellij开发安卓与genymotion配合 [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http: ...
- 关于DexOpt: not all deps represented
最近在做android BSP 4.2的时候遇到一个BUG,编译user 版本的时候,系统刷进手机里面去,无限循环在开机动画,编译userdebug 刷机进去的时候发现正常,于是我先回滚到正常的版本, ...
- Decision Tree
Decision Tree builds classification or regression models in the form of a tree structure. It break d ...