扩展GROUP BY概述
在本章的开头已经简单描述了扩展GROUP BY的应用场景,Oracle扩展GROUP BY允许使用SQL语句对数据汇总结果进行多维展现,从而生成复杂的报表,为决策者提供有效的数据支持。最重要的是,Oracle9i之后,扩展GROUP BY的功能已经趋于完善,能够满足大部分多维数据分析统计的工作。
主要表现在:
本章的表DEPT和EMP都来自于SCOTT用户下,虽然表比较简单,但是足以能说明扩展GROUP BY的功能。下面开始进入主要内容,探讨强大的扩展GROUP BY功能。
ROLLUP
假设有这样的需求:
--需求1实现
SELECT a.dname,b.job,SUM(b.sal) sum_sal
FROM dept a,emp b
WHERE a.deptno = b.deptno
GROUP BY a.dname,b.job
UNION ALL
--需求2实现
SELECT a.dname,NULL, SUM(b.sal) sum_sal
FROM dept a,emp b
WHERE a.deptno = b.deptno
GROUP BY a.dname
UNION ALL
--需求3实现
SELECT NULL,NULL, SUM(b.sal) sum_sal
FROM dept a,emp b
WHERE a.deptno = b.deptno;
DNAME JOB SUM_SAL
---------------------------- ------------------ ----------
SALES MANAGER 2850
SALES CLERK 950
ACCOUNTING MANAGER 2450
ACCOUNTING PRESIDENT 5000
ACCOUNTING CLERK 1300
SALES SALESMAN 5600
RESEARCH MANAGER 2975
RESEARCH ANALYST 3000
RESEARCH CLERK 800
ACCOUNTING 8750
RESEARCH 6775
SALES 9400
24925
已选择13行。
----------------------------------------------------------
Plan hash value: 3113041979
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 17 | 423 | 13 (70)| 00:00:01 |
| 1 | UNION-ALL | | | | | |
| 2 | HASH GROUP BY | | 12 | 336 | 5 (20)| 00:00:01 |
| 3 | NESTED LOOPS | | 12 | 336 | 4 (0)| 00:00:01 |
| 4 | TABLE ACCESS FULL | EMP | 12 | 180 | 3 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 13 | 1 (0)| 00:00:01 |
|* 6 | INDEX UNIQUE SCAN | PK_DEPT | 1 | | 0 (0)| 00:00:01 |
| 7 | HASH GROUP BY | | 4 | 80 | 5 (20)| 00:00:01 |
| 8 | NESTED LOOPS | | 12 | 240 | 4 (0)| 00:00:01 |
| 9 | TABLE ACCESS FULL | EMP | 12 | 84 | 3 (0)| 00:00:01 |
| 10 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 13 | 1 (0)| 00:00:01 |
|* 11 | INDEX UNIQUE SCAN | PK_DEPT | 1 | | 0 (0)| 00:00:01 |
| 12 | SORT AGGREGATE | | 1 | 7 | | |
|* 13 | TABLE ACCESS FULL | EMP | 12 | 84 | 3 (0)| 00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - access("A"."DEPTNO"="B"."DEPTNO")
11 - access("A"."DEPTNO"="B"."DEPTNO")
13 - filter("B"."DEPTNO" IS NOT NULL)
从执行计划可以看出,为了实现这样的需求,需要多次访问EMP、DEPT表以及DEPT表的索引,这里的测试数据很少而且表结构简单,实际应用中表结构可能很复杂,经常是多表关联,数据量可能达到百万级,千万级,甚至上亿,那么使用UNION ALL,明显性能低下,如果使用WITH子句将常规分组的结果固定下来,然后在此基础上再聚合,效率可能比单纯UNION ALL好,但是还是不够完美,现在的需求是对2列进行多维分析,如果是很多列呢?必然增加语句复杂度,类似地,CUBE,GROUPING SETS用UNION ALL改写也有此类问题,何况扩展GROUP BY还提供了很多复杂功能,用UNION ALL改写就更加麻烦了。
试想对于上面的需求,如果Oracle能提供一个这样的分组功能就好了:
2.2 ROLLUP分组
SELECT … GROUP BY ROLLUP(grouping_column_reference_list)
SELECT a.dname,b.job, SUM(b.sal) sum_sal
FROM dept a,emp b
WHERE a.deptno = b.deptno
GROUP BY ROLLUP(a.dname,b.job);
DNAME JOB SUM_SAL
---------------------------- ------------------ ----------
SALES CLERK 950
SALES MANAGER 2850
SALES SALESMAN 5600
SALES 9400
RESEARCH CLERK 800
RESEARCH ANALYST 3000
RESEARCH MANAGER 2975
RESEARCH 6775
ACCOUNTING CLERK 1300
ACCOUNTING MANAGER 2450
ACCOUNTING PRESIDENT 5000
ACCOUNTING 8750
24925
已选择13行。
----------------------------------------------------------
Plan hash value: 503922295
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 12 | 336 | 5 (20)| 00:00:01 |
| 1 | SORT GROUP BY ROLLUP | | 12 | 336 | 5 (20)| 00:00:01 |
| 2 | NESTED LOOPS | | 12 | 336 | 4 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL | EMP | 12 | 180 | 3 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 13 | 1 (0)| 00:00:01 |
|* 5 | INDEX UNIQUE SCAN | PK_DEPT | 1 | | 0 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("A"."DEPTNO"="B"."DEPTNO")
ROLLUP分组具有方向性,从上面的结果看出,ROLLUP(a.dname,b.job)分组的过程是这样的:
分组级别 | 描述 |
a,b,c | 标准分组 |
a,b | 对于每个a,b列值,计算横跨c列的小计 |
a | 对于每个a列值,计算横跨b,c列的小计 |
合计汇总 | 合计 |
另外提一下,其实ROLLUP操作,如果使用HINT: expand_gset_to_union,那么则优化器会将ROLLUP转为对应的UNION ALL操作,其它的GROUPING SETS、CUBE也可以,有兴趣的可以试一下。
ROLLUP语法简单,而且具体处理过程也很简单,除了第1个是标准分组,然后就是列从右到左递减的分组,最后合计。下面实现需求:
SELECT to_char(b.hiredate,yyyy) hire_year,a.dname,b.job, SUM(sal) sum_sal
FROM dept a,emp b
WHERE a.deptno = b.deptno
GROUP BY ROLLUP(to_char(b.hiredate,yyyy),a.dname,b.job);
HIRE_YEA DNAME JOB SUM_SAL
-------- ---------------------------- ------------------ ----------
1980 RESEARCH CLERK 800
1980 RESEARCH 800
1980 800
1981 SALES CLERK 950
1981 SALES MANAGER 2850
1981 SALES SALESMAN 5600
1981 SALES 9400
1981 RESEARCH ANALYST 3000
1981 RESEARCH MANAGER 2975
1981 RESEARCH 5975
1981 ACCOUNTING MANAGER 2450
1981 ACCOUNTING PRESIDENT 5000
1981 ACCOUNTING 7450
1981 22825
1982 ACCOUNTING CLERK 1300
1982 ACCOUNTING 1300
1982 1300
24925
相关行 | 描述 |
第7行 | 对第4、5、6行的所有JOB进行小计 |
第14行 | 对第7行、第13行的所有DNAME、JOB进行小计,当然也相当于对4-6、8-12行所有DNAME、JOB的小计。 |
最后一行 | 对所有入职日期(精确到年)、DNAME、JOB进行合计 |
因为ROLLUP分组过程具有方向性,所以通过改变ROLLUP中列的顺序就可以达到改变报表结果和含义的目的。比如将前面的ROLLUP(dname,job)改为ROLLUP(job,dname)则含义就发生了变化,现在需要查询的就是标准分组、计算每个job的所有部门的小计、最后合计,这里就两个列,也就是小计的含义发生了变化,请看:
SELECT b.job,a.dname, SUM(b.sal) sum_sal
FROM dept a,emp b
WHERE a.deptno = b.deptno
GROUP BY ROLLUP(b.job,a.dname);
JOB DNAME SUM_SAL
------------------ ---------------------------- ----------
CLERK SALES 950
CLERK RESEARCH 800
CLERK ACCOUNTING 1300
CLERK 3050
ANALYST RESEARCH 3000
ANALYST 3000
MANAGER SALES 2850
MANAGER RESEARCH 2975
MANAGER ACCOUNTING 2450
MANAGER 8275
SALESMAN SALES 5600
SALESMAN 5600
PRESIDENT ACCOUNTING 5000
PRESIDENT 5000
24925
已选择15行。
SELECT to_char(b.hiredate,yyyy) hire_year,a.dname,b.job, SUM(sal) sum_sal
FROM dept a,emp b
WHERE a.deptno = b.deptno
GROUP BY to_char(b.hiredate,yyyy),a.dname,ROLLUP(b.job);
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/129114.html
摘要:当向传入一列时,会得到一个总计行。结果当向传递两列时,将会按照这两列进行分组,同时按照第一列的分组结果返回小计行。结果可以看出来结果是按照工厂和部门分别分组汇总的。选择的就表示两列都不为空。 Group By Group By 谁不会啊?这不是最简单的吗?越是简单的东西,我们越会忽略掉他,因为我们不愿意再去深入了解它。1 小时 SQL 极速入门(一)1 小时 SQL 极速入门(二)1 ...
阅读 1247·2023-01-11 13:20
阅读 1555·2023-01-11 13:20
阅读 1008·2023-01-11 13:20
阅读 1675·2023-01-11 13:20
阅读 3968·2023-01-11 13:20
阅读 2510·2023-01-11 13:20
阅读 1305·2023-01-11 13:20
阅读 3474·2023-01-11 13:20