SqlServer:分组查询和聚集函数

聚集函数

聚合函数是用于对表进行记录统计、数据运算的函数,它返回单个值。聚合函数经常与 SELECT 语句的 GROUP BY 子句一起使用,作为分组依据。聚合函数主要有 COUNT(求记录数)、SUM(求和)、AVG(求平均值)、MAX(求最大值)、MIN (求最小值) 5 个。

常用的函数

如下是 5 个常用的聚集函数,这些函数在统计时系统会自动忽略 NULL 值。

聚集函数 功能 格式 数据类型
count 求符合条件的记录数 count(列名) 或者 count(DISTINCT 列名)
MAX 求某一列的最大值 MAX(列名) 列的类型可以比较大小
MIN 求某一列的最小值 MIN(列名) 列的类型可以比较大小
AVG 求某一列的平均值 AVG(列名) 列的类型只能是数值型或者货币型
SUM 求某一列的总和 SUM(列名) 列的类型只能是数值型或者货币型

查询样例

假设此时有成绩表 Score,表中具有以下字段和记录。

样例一

查询选课表中最高分、平均分、最低分,需要使用 MAX、MIN、AVG 3 个聚集函数。

SELECT MAX(DEGREE), AVG(DEGREE), MIN(DEGREE)
FROM Score

样例二

查询“3-105”课程的最高分、平均分和最小成绩,在上一个样例的基础上使用 WHERE 子句进行过滤。

SELECT MAX(DEGREE), AVG(DEGREE), MIN(DEGREE)
FROM Score
WHERE CNO = '3-105'

分组查询

GROUP BY 子句

利用 SELECT 的 GROUP BY 子句,能够对查询结果按照指定的字段进行分组,值相等的记录被分在同一组。GROUP BY 子句往往和 SQL 的聚合函数一起使用,基本格式如下,其中 GROUP BY 后面的列名称为分组依据,根据这个字段的不同值进行分组显示。

SELECT 列名列表
FROM 表名
WHERE 条件
GROUP BY 列名

如果有 GROUP BY 子句,则在 SELECT 后面的列名必须包含在聚合函数中,或者包含在 GROUP BY 子句中,否则系统拒绝执行。

HAVING 子句

如果 SELECT 语句中有 GROUP BY 子句,但是想对汇总查询进行过滤不能使用 WHERE 子句。此时可以使用 HAVING 子句对 GROUP BY 进行限制,对不符合要求的进行过滤分组。HAVING 子句的格式如下,HAVING 子句必须和 GROUP BY 子句配合,且放在 GROUP BY 子句的后面表示分组的前提条件。

HAVING(条件)

HAVING 子句和 WHERE 子句的区别在于作用对象不同,WHERE 子句的作用对象是表,是从表中选择出满足筛选条件的记录,而 HAVING 子句的作用对象是组,是从组中选择出满足筛选条件的记录。

查询样例

样例一

查询各学生的选课数,此时可以对学号 Sno 进行分组,用 Count 函数统计每组有多少记录。

SELECT SNO,COUNT(*)
FROM Score
GROUP BY SNO

样例二

查询选修了3 门以上课程的学生学号,此时需要使用 HAVING 子句对分组进行过滤。

SELECT SNO, COUNT(*)
FROM Score
GROUP BY SNO
HAVING COUNT(*) >= 3

样例三

按学号分组汇总学生的平均分,并按平均分的降序排列,分组后使用 AVG 函数进行聚集,同时用 ORDER BY 子句进行排序。

SELECT SNO 学号, AVG(DEGREE) 平均分
FROM Score
GROUP BY SNO
ORDER BY 平均分 DESC

样例四

假设 Student 表中有如下一些数据:

查询每位学生的最高、最低分、平均分,包括学号、姓名、最高、最低分和平均分,在上述样例的基础上使用 JOIN 子句进行多表查询。注意在 SELECT 后面的列名必须包含在聚合函数中,或者包含在 GROUP BY 子句中,否则系统拒绝执行,因此 GROUP BY 子句中需要给出 Sno 和 Sname 2 个字段。

SELECT S.SNO, SNAME, MAX(DEGREE) 最高分, MIN(DEGREE) 最低分, AVG(DEGREE) 平均分
FROM Score SC
JOIN Student S ON S.SNO = SC.SNO
GROUP BY S.SNO, SNAME

生成汇总值

使用 GROUP BY 子句进行汇总以后,可以使用一些运算符生成汇总值,例如对各个字段聚合的分组输出一个总的汇总信息。

ROLLUP 运算符

使用 GROUP BY 子句和 ROLLUP 操作符时,将在结果集中增加一行显示总和或平均值之类的汇总值。处理 GROUP BY 中字段列表的顺序是从右到左,然后对每个组使用聚合函数,新增的行以 NULL 标识。
注意不能同时使用关键字 ALL 和操作符 ROLLUP,使用 ROLLUP 时要确保出现在 GROUP BY 后的各字段,在数据库环境中具有确定的、有意义的关系。

CUBE 运算符

使用 GROUP BY 子句和 CUBE 操作符,能生成基于 GROUP BY 子句指定的所有字段的可能组合。如果在 GROUP BY 子句中有 n 个字段或表达式,则在结果集中将返回 2^n 种可能的组合,结果集中含有 NULL 的记录代表该记录由 CUBE 操作符生成。
注意不能同时使用关键字 ALL 和操作符 CUBE,使用 CUBE 时要确保出现在 GROUP BY 后的各字段,在数据库环境中具有确定的、有意义的关系。

GROUPING 函数

GROUPING 函数能够帮助识别结果集中的空值是表中原有的空值,还是由 ROLLUP 或 CUBE 生成的含有汇总值的新记录。GROUPING 函数指定字段后将生成一个新的字段,如果该字段返回 1,代表结果集中的这一记录是由 ROLLUP 或 CUBE 生成的,否则该记录原本就存在于数据库的表中。

查询样例

样例一

查询学生信息,包括学号、课程号、成绩以及学生的平均分。此时需要使用 ROLLUP 函数指定需要聚集的列,生成一则汇总信息。

SELECT isnull(CAST(Sno as char(8)), '总平均分') Sno,    
        --若该数据为所有分组的汇总数据,用“总平均分”替换 NULL
	CASE WHEN Sno is null AND Cno IS null THEN '' ELSE isnull(CAST(CNO as char(6)), '平均分') END CNO,
        --若该数据为某个分组的汇总数据,用“平均分”替换 NULL
	AVG(Degree)
FROM Score
GROUP BY GROUPING SETS (ROLLUP(Sno, Cno))

样例二

查询学生信息,包括姓名、课程名、成绩以及每位学生的平均分和每门课程的平均分。由于还需要对每门课程生成汇总记录,因此需要使用操作符 CUBE 生成多个汇总信息。

SELECT CASE WHEN S.Sname is null AND Cno IS null THEN '总平均分' ELSE isnull(CAST(Sname as char(10)), '课程平均分') END Sname,
       --总汇总数据用“总平均分”替换 NULL,Cno 字段汇总数据用“课程平均分”替换 NULL
       CASE WHEN Sname is null AND Cno IS null THEN '' ELSE isnull(CAST(Cno as char(10)), '学生平均分') END Cno,
       --若该数据为某个分组的汇总数据,汇总数据用“学生平均分”替换 NULL
       AVG(Degree) 成绩
FROM Score SC
JOIN Student S ON S.Sno = SC.Sno
GROUP BY Sname, Cno WITH CUBE

样例三

假设此时有成绩表 Course,表中具有以下字段和记录。

查询学生信息,包括学号、姓名、课程号、课程名、成绩以及每位学生的平均分和每门课程的平均分。此时由于需要生成多个汇总数据,需要使用 GROUPING SETS 函数指定需要汇总的数据。

SELECT 
    CASE WHEN SC.Sno IS null AND SC.Cno IS null THEN '总' ELSE isnull(CAST(SC.Sno as char(10)), '课程') END Sno,
    CASE WHEN SC.Sno IS null AND SC.Cno IS null THEN '平均分' ELSE isnull(CAST(S.Sname as char(10)), '平均分') END Sname,
    CASE WHEN SC.Sno IS null AND SC.Cno IS null THEN '' ELSE isnull(CAST(SC.Cno as char(10)), '学生') END Cno,
    CASE WHEN SC.Sno IS null AND SC.Cno IS null THEN '' ELSE isnull(CAST(C.Cname as char(10)), '平均分') END Cname,
    AVG(Degree) 成绩
FROM Score SC
JOIN Student S ON S.Sno = SC.Sno
JOIN Course C ON C.Cno = SC.Cno
GROUP BY GROUPING SETS ((sc.Cno, sc.Sno, Sname, Cname), (SC.Sno, S.Sname), (SC.Cno, C.Cname))

参考资料

《SqlServer 2014 数据库技术实用教程》,胡伏湘、肖玉朝 主编,清华大学出版社

posted @ 2021-05-21 00:18  乌漆WhiteMoon  阅读(3901)  评论(0编辑  收藏  举报