.Net程序员学用Oracle系列(21):分组查询(GROUP BY)
- 1、GROUP BY 标准分组
- 1.1、GROUP BY 概述
- 1.2、WHERE 和 HAVING 的区别?
- 2、GROUP BY 扩展分组
- 2.1、ROLLUP 分组
- 2.2、CUBE 分组
- 2.3、GROUPING SETS 分组
- 2.4、复杂分组(组合列分组、重复列分组、连接分组)
- 3、GROUP BY 扩展函数
- 3.1、GROUPING 函数
- 3.2、GROUPING_ID 函数
- 3.3、GROUP_ID 函数
- 4、总结
1、GROUP BY 标准分组
为了便于讲述,我将把简单 GROUP BY 子句的分组称之为标准分组,把 GROUP BY 子句中出现的列(或表达式)称之为分组列。
1.1、GROUP BY 概述
在分组查询中,GROUP BY 子句的作用就是按指定的(一或多个)列或表达式的值将选定行集进行分组,并针对每一组返回一行从组中收集到的数据。基本语法:
SELECT expression1, expression2, ... expression_n,
aggregate_function (aggregate_expression)
FROM tables
[WHERE conditions]
GROUP BY expression1, expression2, ... expression_n
[HAVING having_condition];
简单示例:
SELECT t.dept_code,
MAX(t.post_salary) max_salary, -- 部门最高岗位工资
MIN(t.post_salary) min_salary, -- 部门最低岗位工资
AVG(t.post_salary) avg_salary, -- 部门平均岗位工资
SUM(t.post_salary) sum_salary, -- 部门岗位工资之和
COUNT(t.post_salary) cnt_salary -- 部门工资份数
FROM demo.t_staff t
GROUP BY t.dept_code
HAVING AVG(t.post_salary)>3500
ORDER BY t.dept_code;
注意事项:
- 1、SELECT 子句中只能出现分组列或聚合函数或常量。
- 2、HAVING 子句中只能出现分组列或聚合函数或常量。
- 3、GROUP BY 子句中只能出现标量基元类型(如 VARCHAR2、NUMBER、DATE 等)的列或常量,不能出现 BLOB、CLOB 等类型的列。
- 4、实际上分组列可以不出现在 SELECT 子句中,但这可能会让查询结果含义不明确。
- 5、在 SELECT、HAVING、GROUP BY 子句中还可以出现某些特殊的函数,如 SYSDATE(感觉没啥意义)。
1.2、WHERE 和 HAVING 的区别?
我首先要说明的是:“WHERE 和 HAVING 的区别?”这绝对是一个有问题的问题!因为它两根本就没有可比性,实际上问这种问题的人,往往 SQL 基础也不够扎实。
在包含 GROUP BY 子句的查询语句中:WHERE 子句的作用是在对查询结果分组前过滤行数据,将不符合条件的行去掉;而 HAVING 子句的作用是在对查询结果分组后过滤组数据,将不符合条件的组去掉。换句话说,因为聚合函数的作用是提供有关组的信息,还没分组之前当然是无法提供组的信息的,也就是说 WHERE 子句中无法使用聚合函数,这也正是会出现 HAVING 子句的原因。有了 HAVING 子句,我们就可以很方便的在分组之后对组的数据进行过滤了。一般来说,能用 WHERE 的过滤的就不应该用 HAVING 过滤!
2、GROUP BY 扩展分组
在实际业务开发中,只有标准分组可能还不够,往往还需要更多维度的小计、合计。针对这类需求,Oracle 提供了丰富的扩展分组功能;尽管用 UNION ALL 一般也能实现类似效果,但不够灵活且性能比较低。
2.1、ROLLUP 分组
ROLLUP 是 GROUP BY 子句的一种扩展,它允许计算标准分组及部分维度的小计、合计。语法:
GROUP BY ROLLUP(grouping_column_reference_list)
ROLLUP 的计算结果与括号中指定列的顺序有关,因为 ROLLUP 的分组过程具有方向性,先计算标准分组,然后从右到左递减计算更高一级的小计,直到所有列被计算完,最后计算合计。当 ROLLUP 中指定 n 列时,整个计算过程中分组方式有 n+1 种。如GROUP BY ROLLUP(A,B)
的分组过程相当于是:第 1 步按 GROUP BY(A,B) 分组求小计,第 2 步按 GROUP BY(A,NULL) 分组求小计,第 3 步按 GROUP BY(NULL,NULL) 分组求合计。
示例 1:
SELECT t.dept_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY ROLLUP(t.dept_code);
结果:
DEPT_CODE SUM_SALARY MAX_SALARY
-------------------------------------------------- ---------- ----------
010102 13500 7500
010103 7850 5050
21350 7500
示例 2:
SELECT t.dept_code,t.post_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY ROLLUP(t.dept_code,t.post_code);
结果:
DEPT_CODE POST_CODE SUM_SALARY MAX_SALARY
---------------------------------------------- -------------------------------------------- ---------- ----------
010102 P40 7500 7500
010102 P50 6000 6000
010102 13500 7500
010103 P40 5050 5050
010103 P50 2800 2800
010103 7850 5050
21350 7500
示例 3,部分 ROLLUP 分组(不需要某些小计、合计时可用该种写法):
SELECT t.dept_code,t.post_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY t.dept_code,ROLLUP(t.post_code);
结果:
DEPT_CODE POST_CODE SUM_SALARY MAX_SALARY
--------------------------------------------- -------------------------------------------- ---------- ----------
010102 P40 7500 7500
010102 P50 6000 6000
010102 13500 7500
010103 P40 5050 5050
010103 P50 2800 2800
010103 7850 5050
2.2、CUBE 分组
CUBE 是 GROUP BY 子句的一种扩展,它允许计算标准分组及所有维度的小计、合计。语法:
GROUP BY CUBE(grouping_column_reference_list)
CUBE 会对所有可能的分组进行统计,从而生成交叉报表。CUBE 比 ROLLUP 的分组更多,且包含了 ROLLUP 的统计结果,且计算结果与分组列的顺序无关,但如果列顺序不同,默认的结果排序会有不同。当 CUBE 中指定 n 列时,整个计算过程中分组方式有 2 的 n 次方种。如GROUP BY CUBE(A,B)
相当于:按 GROUP BY(A,B) 分组的小计,加按 GROUP BY(A,NULL) 分组的小计,加按 GROUP BY(NULL,B) 分组的小计,加按 GROUP BY(NULL,NULL) 分组的合计。
示例 1:
SELECT t.dept_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY CUBE(t.dept_code);
结果:
DEPT_CODE SUM_SALARY MAX_SALARY
-------------------------------------------------- ---------- ----------
21350 7500
010102 13500 7500
010103 7850 5050
示例 2:
SELECT t.dept_code,t.post_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY CUBE(t.dept_code,t.post_code);
结果:
DEPT_CODE POST_CODE SUM_SALARY MAX_SALARY
--------------------------------------------- ------------------------------------------ ---------- ----------
21350 7500
P40 12550 7500
P50 8800 6000
010102 13500 7500
010102 P40 7500 7500
010102 P50 6000 6000
010103 7850 5050
010103 P40 5050 5050
010103 P50 2800 2800
示例 3,部分 CUBE 分组(不需要某些小计、合计时可用该种写法):
SELECT t.dept_code,t.post_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY t.dept_code,CUBE(t.post_code);
结果:
DEPT_CODE POST_CODE SUM_SALARY MAX_SALARY
-------------------------------------------- ---------------------------------------- ---------- ----------
010102 13500 7500
010102 P40 7500 7500
010102 P50 6000 6000
010103 7850 5050
010103 P40 5050 5050
010103 P50 2800 2800
2.3、GROUPING SETS 分组
GROUPING SETS 是 GROUP BY 子句的一种扩展,它允许一次计算多个标准分组的小计。语法:
GROUP BY GROUPING SETS(grouping_column_reference_list)
GROUPING SETS 的计算结果和分组列的顺序无关,结果集排序也和分组列的顺序无关。当 GROUPING SETS 中指定 n 列时,整个计算过程中分组方式有 n 种。如GROUPING SETS(A,B,C)
相当于 GROUP BY A、GROUP BY B 和 GROUP BY C 这 3 个分组 UNION ALL 的结果。
示例 1:
SELECT t.dept_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY GROUPING SETS(t.dept_code);
结果:
DEPT_CODE SUM_SALARY MAX_SALARY
-------------------------------------------------- ---------- ----------
010102 13500 7500
010103 7850 5050
示例 2:
SELECT t.dept_code,t.post_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY GROUPING SETS(t.dept_code,t.post_code);
结果:
DEPT_CODE POST_CODE SUM_SALARY MAX_SALARY
--------------------------------------------- ------------------------------------------ ---------- ----------
010102 13500 7500
010103 7850 5050
P50 8800 6000
P40 12550 7500
示例 3,部分 GROUPING SETS 分组:
SELECT t.dept_code,t.post_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY t.dept_code,GROUPING SETS(t.post_code);
结果:
DEPT_CODE POST_CODE SUM_SALARY MAX_SALARY
-------------------------------------------- ----------------------------------------- ---------- ----------
010103 P40 5050 5050
010102 P40 7500 7500
010102 P50 6000 6000
010103 P50 2800 2800
示例 4,GROUPING SETS 能够接受 ROLLUP 和 CUBE 作为它的参数;GROUPING SETS 只对单列进行分组,而不提供合计的功能,如果需要 GROUPING SETS 提供合计,可用 ROLLUP 或 CUBE 作参数来提供合计功能(注意 ROLLUP 和 CUBE 不接受 GROUPING SETS 作参数,ROLLUP 和 CUBE 之间互相作为参数也不可以):
SELECT t.dept_code,t.post_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY GROUPING SETS(ROLLUP(t.dept_code),ROLLUP(t.post_code));
结果:
DEPT_CODE POST_CODE SUM_SALARY MAX_SALARY
-------------------------------------------- ------------------------------------------ ---------- ----------
010102 13500 7500
010103 7850 5050
P50 8800 6000
P40 12550 7500
21350 7500
21350 7500
2.4、复杂分组(组合列分组、重复列分组、连接分组)
简单来说:组合列分组就是允许 ROLLUP、CUBE 和 GROUPING SETS 中可以有多个列或列组合;重复列分组就是允许 GROUP BY 后面重复出现分组列;连接分组就是允许 GROUP BY 后面有多个 ROLLUP、CUBE 或 GROUPING SETS。
组合列分组有过滤某些小计或计算一些额外的小计的功能。前面的部分 ROLLUP 和 部分 CUBE 都没有合计,使用组合列分组既可以实现部分 ROLLUP 或 部分 CUBE 的功能,还能有合计。如ROLLUP(A,(B,C))
,既能过滤 B、C 的小计,还能计算 ABC 的合计。
连接分组的分组级别是由所有 ROLLUP、CUBE 或 GROUPING SETS 分组的级别组成的笛卡尔积。如ROLLUP(A,B),ROLLUP(C,D,E)
的分组级别是 (2+1)×(3+1)=12,CUBE(A,B),CUBE(C,D,E)
的分组级别是 (4)×(8)=32,CUBE(A,B),CUBE(C,D,E)
的分组级别是 (4)×(8)=32。
按我的理解来说:复杂分组无非也就是对标准扩展分组的综合运用。在实际开发中,可能会遇到一些仅使用标准扩展分组无法实现的需求,这时候就可以考虑灵活的运用标准扩展分组,通过复杂分组来实现。
3、GROUP BY 扩展函数
3.1、GROUPING 函数
GROUPING 语法:GROUPING(expr)
。因为原始数据中可能存在 NULL,且小计或合计的值也可能为 NULL,这样一来就显得数据比较混乱了。当该函数出现在 SELECT 子句中时,如果聚集行的 expr 为 NULL,它就返回 1;如果常规行的 expr 为 NULL,它就返回 0。通常将一个分组列作为该函数的参数,然后通过判断它的返回值来区分聚集行与常规行,从而进一步对结果集美化或过滤。示例:
SELECT DECODE(GROUPING(t.dept_code),1,'合计',t.dept_code) dept_code,
SUM(t.post_salary) sum_salary,GROUPING(t.dept_code) gd
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY ROLLUP(t.dept_code);
结果:
DEPT_CODE SUM_SALARY GD
-------------------------------------------------- ---------- ----------
010102 13500 0
010103 7850 0
合计 21350 1
3.2、GROUPING_ID 函数
GROUPING_ID 语法:GROUPING_ID(expr [, expr ]...)
。当该函数出现在 SELECT 子句中时,它将返回与行相关联的 GROUPING 位向量对应的数值。GROUPING_ID 函数按从左到右的顺序计算,如果此列是分组列,则为 0,如果是小计或合计则为 1,然后按列的顺序将计算结果组成二进制序列(位向量),最后将位向量转化为十进制数。GROUPING_ID 函数在功能上等效于多个 GROUPING 函数的结果,有了 GROUPING_ID 就不必再写多个 GROUPING 了,也使得行过滤条件更容易表达。当查询结果有多个聚合级别时,该函数特别有用,可通过它的返回值来排序和过滤结果集。示例:
SELECT t.dept_code,t.post_code,SUM(t.post_salary) sum_salary,
GROUPING_ID(t.dept_code) gd,
GROUPING_ID(t.post_code) gp,
GROUPING_ID(t.dept_code,t.post_code) gdp,
GROUPING_ID(t.post_code,t.dept_code) gpd
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY CUBE(t.dept_code,t.post_code)
ORDER BY GROUPING_ID(t.dept_code,t.post_code);
结果(结果集相当于是按 GDP 升序排序):
DEPT_CODE POST_CODE SUM_SALARY GD GP GDP GPD
----------------------------- ------------------------ ---------- ---------- ---------- ---------- ----------
010102 P40 7500 0 0 0 0
010103 P40 5050 0 0 0 0
010102 P50 6000 0 0 0 0
010103 P50 2800 0 0 0 0
010103 7850 0 1 1 2
010102 13500 0 1 1 2
P50 8800 1 0 2 1
P40 12550 1 0 2 1
21350 1 1 3 3
3.3、GROUP_ID 函数
GROUP_ID 语法:GROUP_ID()
。当该函数出现在 SELECT 子句中时,如果结果集中存在 n 个重复,那么它将返回范围从 0 到 n-1 中的数字,这对于从查询结果中剔除重复分组来说非常有用。示例(一般通过HAVING GROUP_ID()<1
将重复行全部剔除):
SELECT t.dept_code,SUM(t.post_salary) sum_salary,GROUP_ID() group_id
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY ROLLUP(t.dept_code),CUBE(t.dept_code);
结果:
DEPT_CODE SUM_SALARY GROUP_ID
-------------------------------------------------- ---------- ----------
010102 13500 0
010103 7850 0
010102 13500 2
010103 7850 2
010102 13500 1
010103 7850 1
21350 0
4、总结
本文主要讲述了 Oracle 中分组查询的标准分组、扩展分组、扩展函数等 GROUP BY 相关的知识点。
本文链接:http://www.cnblogs.com/hanzongze/p/oracle-group-by.html
版权声明:本文为博客园博主 韩宗泽 原创,作者保留署名权!欢迎通过转载、演绎或其它传播方式来使用本文,但必须在明显位置给出作者署名和本文链接!本人初写博客,水平有限,若有不当之处,敬请批评指正,谢谢!
.Net程序员学用Oracle系列(21):分组查询(GROUP BY)的更多相关文章
- .Net程序员学用Oracle系列(9):系统函数(上)
<.Net程序员学用Oracle系列:导航目录> 本文大纲 1.字符函数 1.1.字符函数简介 1.2.语法说明及案例 2.数字函数 2.1.数字函数简介 2.2.语法说明及案例 3.日期 ...
- .Net程序员学用Oracle系列(10):系统函数(下)
<.Net程序员学用Oracle系列:导航目录> 本文大纲 1.转换函数 1.1.TO_CHAR 1.2.TO_NUMBER 1.3.TO_DATE 1.4.CAST 2.近似值函数 2. ...
- .Net程序员学用Oracle系列(11):系统函数(下)
1.聚合函数 1.1.COUNT 函数 1.2.SUM 函数 1.3.MAX 函数 1.4.MIN 函数 1.5.AVG 函数 2.ROWNUM 函数 2.1.ROWNUM 函数简介 2.2.利用 R ...
- .Net程序员学用Oracle系列(15):DUAL、ROWID、NULL
1.DUAL 表 2.ROWID 类型 2.1.利用 ROWID 查询数据 2.2.利用 ROWID 更新数据 3.NULL 值 3.1.NULL 与空字符串 3.2.NULL 与函数 3.3.NUL ...
- .Net程序员学用Oracle系列(30):零碎补充、最后总结(The End)
1.同义词 2.Flashback 技术 3.连接字符串的写法 4.转义字符 & 特殊运算符 5.文件类型 6.查看参数 & 修改参数 7.AWR 工具 8.学习方法 & 学习 ...
- .Net程序员学用Oracle系列(1):导航目录
本人从事基于 Oracle 的 .Net 企业级开发近三年,在此之前学习和使用的都是 (MS)SQL Server.未曾系统的了解过 Oracle,所以长时间感到各种不习惯.不方便.怪异和不解,常会遇 ...
- .Net程序员学用Oracle系列(2):准备测试环境
<.Net程序员学用Oracle系列:导航目录> 本文大纲 1.创建说明 1.1.为什么要创建的测试环境? 1.2.了解 Oracle 实例的默认用户 2.创建环境 2.1.创建基本环境 ...
- .Net程序员学用Oracle系列(6):表、字段、注释、约束、索引
<.Net程序员学用Oracle系列:导航目录> 本文大纲 1.表 1.1.创建表 1.2.修改表 & 删除表 2.字段 2.1.添加字段 2.2.修改字段 & 删除字段 ...
- .Net程序员学用Oracle系列(7):视图、函数、过程、包
<.Net程序员学用Oracle系列:导航目录> 本文大纲 1.视图 1.1.创建视图 2.函数 2.1.创建函数 2.2.调用函数 3.过程 3.1.创建过程 3.2.调用过程 4.包 ...
随机推荐
- ARPU值分析
每用户平均收入(“ARPU)它由一个消费群体消费总额除以群体总人数得到. 活跃付费账户(“APA”) 平均同时在线玩家人数(“ACU”) 最高同时在线玩家人数(“PCU”)同时在线玩家数最高峰. 千人 ...
- border-radius是向元素添加圆角边框的方法
border-radius:10px; /* 所有角都使用半径为10px的圆角 */ border-radius: 5px 4px 3px 2px; /* 四个半径值分别是左上角.右上角.右下角和左下 ...
- 以Tomcat+Mysql为例,实现Docker多容器连接
Docker提供了多个容器直接访问的方法,最简单的方式是直接使用端口映射-p参数指定映射的端口或者-P映射所有端口,多个容器直接通过网络端口进行访问. 但网络端口映射方式并不是Docker中连接多个容 ...
- 与中国最顶尖sharepoint工程师共舞
最近又跳了,来到某家外企.自以为善能称心如意,谁知乃井里之蛙. 给我的最大感触是,做sharepoint一定要做过非常大型的部署开发,没有经过这种淬炼,天天闷声研究,做一些页面功能,对技术提升毫无帮助 ...
- 爱你不容易 —— Stream详解
作为前端,我们常常会和 Stream 有着频繁的接触.比如使用 gulp 对项目进行构建的时候,我们会使用 gulp.src 接口将匹配到的文件转为 stream(流)的形式,再通过 .pipe() ...
- 游戏音频技术备忘 (四) Wwise Unreal Engine 集成代码浅析 (一)
在Engine\Plugins\Wwise\Source下为主要Wwise相关代码,AkAudio文件夹下为运行时相关代码,AudiokineticTools下为编辑器工具相关代码,Audiokine ...
- ElasticSearch5集群部署指南
本文简要介绍ES5版本集群部署时的要点. 更多相关信息请参阅官网. 部分配置未在生产环境体现. 生产中2个集群20台centOS,总数据15TB,90亿条. 实时写入5000条/s, 最大7万/s. ...
- python 模块加载错误总结
在运行yum时提示如下错误 There was a problem importing one of the Python modules required to run yum. The error ...
- int与integer的区别(基本数据类型与引用数据类型)
一.先说说int与integer的区别 int 是基本数据类型,默认值为0,不需要进行实例化 integer 是引用数据类型,是int的封装类型,默认值为null,创建该类型需要进行实例化. 基本数据 ...
- java测试之文件操作
package filestream; import java.io.File; public class FileTester { public static void main(String [] ...