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)的更多相关文章

  1. .Net程序员学用Oracle系列(9):系统函数(上)

    <.Net程序员学用Oracle系列:导航目录> 本文大纲 1.字符函数 1.1.字符函数简介 1.2.语法说明及案例 2.数字函数 2.1.数字函数简介 2.2.语法说明及案例 3.日期 ...

  2. .Net程序员学用Oracle系列(10):系统函数(下)

    <.Net程序员学用Oracle系列:导航目录> 本文大纲 1.转换函数 1.1.TO_CHAR 1.2.TO_NUMBER 1.3.TO_DATE 1.4.CAST 2.近似值函数 2. ...

  3. .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 ...

  4. .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 ...

  5. .Net程序员学用Oracle系列(30):零碎补充、最后总结(The End)

    1.同义词 2.Flashback 技术 3.连接字符串的写法 4.转义字符 & 特殊运算符 5.文件类型 6.查看参数 & 修改参数 7.AWR 工具 8.学习方法 & 学习 ...

  6. .Net程序员学用Oracle系列(1):导航目录

    本人从事基于 Oracle 的 .Net 企业级开发近三年,在此之前学习和使用的都是 (MS)SQL Server.未曾系统的了解过 Oracle,所以长时间感到各种不习惯.不方便.怪异和不解,常会遇 ...

  7. .Net程序员学用Oracle系列(2):准备测试环境

    <.Net程序员学用Oracle系列:导航目录> 本文大纲 1.创建说明 1.1.为什么要创建的测试环境? 1.2.了解 Oracle 实例的默认用户 2.创建环境 2.1.创建基本环境 ...

  8. .Net程序员学用Oracle系列(6):表、字段、注释、约束、索引

    <.Net程序员学用Oracle系列:导航目录> 本文大纲 1.表 1.1.创建表 1.2.修改表 & 删除表 2.字段 2.1.添加字段 2.2.修改字段 & 删除字段 ...

  9. .Net程序员学用Oracle系列(7):视图、函数、过程、包

    <.Net程序员学用Oracle系列:导航目录> 本文大纲 1.视图 1.1.创建视图 2.函数 2.1.创建函数 2.2.调用函数 3.过程 3.1.创建过程 3.2.调用过程 4.包 ...

随机推荐

  1. 6.00.1x Introduction to computation

    6.00 Introduction to Computer Science                  and  Programming • Goal: –Become skillful at ...

  2. linux下安装TensorFlow(centos)

    一.python安装 centos自带python2.7.5,这一步可以省略掉. 二.python-pip pip--python index package,累世linux的yum,安装管理pyth ...

  3. Word常用实用知识3

    纯手打,可能有错别字,使用的版本是office Word 2013 转载请注明出处 http://www.cnblogs.com/hnnydxgjj/p/6322813.html,谢谢. 分页符分页 ...

  4. “权限系统_基于HUI”的简单介绍和交流

    昂,最近比较闲,写了个权限系统. 后端框架还是老样子,基于本人自己搭建的后台基础开发框架"Spring_Mvc_EF":前端框架,我挑选了一阵子,最后选用了HUI前端开发框架,因为 ...

  5. Wireshark网络抓包(二)——过滤器

    一.捕获过滤器 选中捕获选项后,就会弹出下面这个框,在红色输入框中就可以编写过滤规则. 1)捕获单个IP地址 2)捕获IP地址范围 3)捕获广播或多播地址 4)捕获MAC地址 5)捕获所有端口号 6) ...

  6. 使用fullPage.js遇到的问题以及翻译

    使用fullPage.js做一简单页面,遇一古怪问题:.section中的h1标签始终被一插件生成的标签包裹,导致样式调整好不困难!花费数小时排查为何会生成这样一个标签,最终在fullPage.js的 ...

  7. ubuntu16.04无法设置选择wifi的解决办法

    在公司上班一直连接的有线,直到昨天拿回家才发现ubuntu无法选择使用wifi上网,这让人非常无奈,截图类似如下: 而正常情况下我们应该在启用联网的下面有wifi链接的选项,如图: 我隐约猜测是和驱动 ...

  8. Ionic start 创建项目报错 Error with start undefined

    转自:http://blog.csdn.net/wenzigui_qy/article/details/52874542 在Installing npm packages的时候报错,如下: Insta ...

  9. 访问量分类统计(QQ,微信,微博,网页,网站APP,其他)

    刚准备敲键盘,突然想起今天已经星期五了,有点小兴奋,一周又这么愉快的结束,又可以休息了,等等..我好像是来写Java博客的,怎么变成了写日记,好吧,言归正传. 不知道大家有没有遇到过这样的需求:统计一 ...

  10. 来一波CSS兼容问题小总结吧

    1.DOCTYPE 影响 CSS 处理; 2.火狐 谷歌等浏览器 设置 padding 后, div 会增加 height 和 width, 但 IE 不会, 故需要用 !important 多设一个 ...