Oracle SQL篇(四)group by 分组与分组的加强 rollup
分组操作group by 和分组的强化(rollup)
分组操作和分组函数的使用,对于编写SQL语句的人来说,是最基本的概念。
我们来看下面的例子:
在这里我们使用员工表EMP
scott@DB01> select * from emp;
EMPNO
ENAME
JOB
MGR
HIREDATE
SAL
COMM
DEPTNO
---------- ---------- --------- ---------- -------------------
---------- ---------- ----------
7369
SMITH
CLERK
7902 1980-12-17
00:00:00
800
20
7499
ALLEN
SALESMAN
7698 1981-02-20
00:00:00
1600
300
30
7521
WARD
SALESMAN
7698 1981-02-22
00:00:00
1250
500
30
7566
JONES
MANAGER
7839 1981-04-02
00:00:00
2975
20
7654
MARTIN
SALESMAN
7698 1981-09-28
00:00:00
1250
1400
30
7698
BLAKE
MANAGER
7839 1981-05-01
00:00:00
2850
30
7782
CLARK
MANAGER
7839 1981-06-09
00:00:00
2450
10
7788
SCOTT
ANALYST
7566 1987-04-19
00:00:00
3000
20
7839
KING
PRESIDENT
1981-11-17
00:00:00
5000
10
7844
TURNER
SALESMAN
7698 1981-09-08
00:00:00
1500
0
30
7876
ADAMS
CLERK
7788 1987-05-23
00:00:00
1100
20
7900
JAMES
CLERK
7698 1981-12-03
00:00:00
950
30
7902
FORD
ANALYST
7566 1981-12-03
00:00:00
3000
20
7934
MILLER
CLERK
7782 1982-01-23
00:00:00
1300
10
14 rows selected.
在员工表中有14条记录,即14个员工,我们可以看到,这14个员工分别属于3个部门(10,20,30),我们可以提出求EMP表中,每个部门的员工薪水总和
scott@DB01> select deptno,sum(sal) tsal
2
from emp
3
group by deptno;
DEPTNO
TSAL
---------- ----------
30
9400
20
10875
10
8750
在这里稍微需要注意的是:select 列表里如果出现列的话,那在group
by语句中同样需要列名,并且只能是列名本身,不能是列的别名。group
by语句可以说是oracle语句里最严格的语句,后面只能跟列的真名,别名、位置号、函数、表达式、子查询
都不被允许。当然如果只考虑实现这里已经做到了,如果我们深入了解一点的话,分组对于数据库来说是要消耗资源的,比如cpu、内存
在oracle9i之前
,分组操作内部主要通过排序来实现,10刚开始,采用hash的算法实现,我们看一下10g下,让面语句的执行计划
scott@DB01> set autotrace trace exp
scott@DB01> /
Execution Plan
----------------------------------------------------------
Plan hash value: 4067220884
---------------------------------------------------------------------------
| Id |
Operation
| Name | Rows | Bytes | Cost (%CPU)|
Time
|
---------------------------------------------------------------------------
| 0 | SELECT
STATEMENT
|
| 14
| 364
|
4 (25)| 00:00:01 |
| 1 | HASH
GROUP
BY
|
| 14
| 364
|
4 (25)| 00:00:01 |
| 2
| TABLE ACCESS FULL|
EMP
| 14
| 364
|
3 (0)| 00:00:01 |
---------------------------------------------------------------------------
Note
-----
- dynamic sampling used for
this statement
其实在有些情况下,我们可以避免hash或是sort的发生,也可以实现分组查询的效果,比如说通过索引,当然这需要你有适当的索引存在。
我们来看下面的演示:
scott@DB01> set autotrace off
scott@DB01> create table s_test(id
number,name varchar2(10),sal number);
Table created.
scott@DB01> begin
2
for i in 1..20000 loop
3
insert into s_test values(i,i||'name',i*10);
4
end loop;
5
commit;
6
end;
7
/
PL/SQL procedure successfully completed.
scott@DB01> /
PL/SQL procedure successfully completed.
scott@DB01> /
PL/SQL procedure successfully completed.
scott@DB01> select count(*) from s_test;
COUNT(*)
----------
60000
我在这里建了一张表s_test,分3次往表里插入数据1-20000,现在我的需求是,找到表里100-120的记录,以及他们出现的次数
scott@DB01> select id,name,count(*) from s_test
where id>=100 and id<=120 group by
id,name;
ID
NAME
COUNT(*)
---------- ---------- ----------
115
115name
3
101
101name
3
103
103name
3
106
106name
3
109
109name
3
118
118name
3
105
105name
3
114
114name
3
102
102name
3
104
104name
3
112
112name
3
116
116name
3
100
100name
3
110
110name
3
113
113name
3
117
117name
3
119
119name
3
107
107name
3
108
108name
3
111
111name
3
120
120name
3
21 rows selected.
我们来看一下语句的执行计划
scott@DB01> set autotrace trace exp
scott@DB01> /
Execution Plan
----------------------------------------------------------
Plan hash value: 752916570
-----------------------------------------------------------------------------
| Id |
Operation
| Name | Rows
| Bytes | Cost (%CPU)|
Time
|
-----------------------------------------------------------------------------
| 0 | SELECT
STATEMENT
|
| 163 | 3260
|
58 (6)| 00:00:01 |
| 1 | HASH
GROUP
BY
|
| 163 | 3260
|
58 (6)| 00:00:01 |
|* 2 | TABLE
ACCESS FULL| S_TEST | 163
| 3260
|
57 (4)| 00:00:01 |
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 -
filter("ID">=100 AND "ID"<=120)
Note
-----
- dynamic sampling used for
this statement
在执行计划中我们发现,成本Cost是58,还有cpu的消耗,在执行计划的第2步,我们发现为了实现分组,oracle做了hash。接下来我们建一个组合索引看看
scott@DB01> create index s_id_n_idx on
s_test(id,name);
Index created.
scott@DB01> select id,name,count(*) from s_test
where id>=100 and id<=120 group by
id,name;
Execution Plan
----------------------------------------------------------
Plan hash value: 826362002
-----------------------------------------------------------------------------------
| Id |
Operation
|
Name
| Rows | Bytes | Cost (%CPU)|
Time
|
-----------------------------------------------------------------------------------
| 0 | SELECT
STATEMENT
|
| 63
| 1260
|
2 (0)| 00:00:01 |
| 1 | SORT
GROUP BY
NOSORT|
| 63
| 1260
|
2 (0)| 00:00:01 |
|* 2 | INDEX
RANGE SCAN | S_ID_N_IDX
| 63
| 1260
|
2 (0)| 00:00:01 |
-----------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 -
access("ID">=100 AND "ID"<=120)
filter("ID">=100 AND "ID"<=120)
Note
-----
- dynamic sampling used for
this statement
在第一步中,应该做的排序并没有做 SORT GROUP BY NOSORT,这样就节省了cpu。
当然在这个例子当中,我们发现了一个重要的问题,就是语句的成本急剧下降,当然,这是通过索引,改变了数据的访问方法造成的,以后有机会在讨论索引的时候,我们会展开来说。
我们接下来看这样一个需求,根据表里的deptno和job求分组,得到每个job下的薪水综合,然后在部门级别做汇总,求小计,在整张表汇总,求总计
scott@DB01> select deptno,job,empno,ename,sal from
emp order by deptno,job;
DEPTNO
JOB
EMPNO
ENAME
SAL
---------- --------- ---------- ---------- ----------
10
CLERK
7934
MILLER
1300
10
MANAGER
7782
CLARK
2450
10
PRESIDENT
7839
KING
5000
20
ANALYST
7788
SCOTT
3000
20
ANALYST
7902
FORD
3000
20
CLERK
7876
ADAMS
1100
20
CLERK
7369
SMITH
800
20
MANAGER
7566
JONES
2975
30
CLERK
7900
JAMES
950
30
MANAGER
7698
BLAKE
2850
30
SALESMAN
7654
MARTIN
1250
30
SALESMAN
7521
WARD
1250
30
SALESMAN
7499
ALLEN
1600
30
SALESMAN
7844
TURNER
1500
其实需求本身很简单,如果仅仅是为了实现的话,使用集合并运算符union就可以了,不过union的效率在这里是非常的低。
scott@DB01> select deptno,job,sum(sal) tsal from emp
group by deptno,job
2 union
3 select
deptno,to_char(null),sum(sal) from emp group by
deptno
4 union
5 select
to_number(null),to_char(null),sum(sal) from emp;
DEPTNO
JOB
TSAL
---------- --------- ----------
10
CLERK
1300
10
MANAGER
2450
10
PRESIDENT
5000
10
8750
20
ANALYST
6000
20
CLERK
1900
20
MANAGER
2975
20
10875
30
CLERK
950
30
MANAGER
2850
30
SALESMAN
5600
30
9400
29025
13 rows selected.
为了得到比较高效的sql,我们可以借助于oracle分组里面的rollup来实现,我们可以得到同样的效果
scott@DB01> select deptno,job,sum(sal) tsal from emp
group by rollup(deptno,job);
DEPTNO
JOB
TSAL
---------- --------- ----------
10
CLERK
1300
10
MANAGER
2450
10
PRESIDENT
5000
10
8750
20
CLERK
1900
20
ANALYST
6000
20
MANAGER
2975
20
10875
30
CLERK
950
30
MANAGER
2850
30
SALESMAN
5600
30
9400
29025
13 rows selected.
第一直观的表现,使用rollup要比使用分组再union的方法语句简单很多,更重要的是,我们只对emp访问了一次。
为了进一步比较,我们来看一下语句的执行计划
scott@DB01> set autotrace trace exp
scott@DB01> select deptno,job,sum(sal) tsal from emp
group by deptno,job
2 union
3 select
deptno,to_char(null),sum(sal) from emp group by
deptno
4 union
5 select
to_number(null),to_char(null),sum(sal) from emp;
Execution Plan
----------------------------------------------------------
Plan hash value: 3412076862
-----------------------------------------------------------------------------
| Id |
Operation
| Name | Rows | Bytes | Cost (%CPU)|
Time
|
-----------------------------------------------------------------------------
| 0 | SELECT
STATEMENT
|
| 29
| 825
|
14 (79)| 00:00:01 |
| 1 | SORT
UNIQUE
|
| 29
| 825
|
14 (79)| 00:00:01 |
| 2
|
UNION-ALL
|
|
|
|
|
|
| 3
| HASH GROUP
BY
|
| 14
| 448
|
5 (40)| 00:00:01 |
| 4
|
TABLE ACCESS FULL| EMP
| 14
| 448
|
3 (0)| 00:00:01 |
| 5
| HASH GROUP
BY
|
| 14
| 364
|
5 (40)| 00:00:01 |
| 6
|
TABLE ACCESS FULL| EMP
| 14
| 364
|
3 (0)| 00:00:01 |
| 7
| SORT
AGGREGATE
|
|
1 | 13
|
4 (25)| 00:00:01 |
| 8
|
TABLE ACCESS FULL| EMP
| 14
| 182
|
3 (0)| 00:00:01 |
-----------------------------------------------------------------------------
Note
-----
- dynamic sampling used for
this statement
scott@DB01> select deptno,job,sum(sal) tsal from emp
group by rollup(deptno,job);
Execution Plan
----------------------------------------------------------
Plan hash value: 52302870
-----------------------------------------------------------------------------
| Id |
Operation
| Name | Rows | Bytes | Cost (%CPU)|
Time
|
-----------------------------------------------------------------------------
| 0 | SELECT
STATEMENT
|
| 14
| 448
|
4 (25)| 00:00:01 |
| 1 | SORT
GROUP BY
ROLLUP|
| 14
| 448
|
4 (25)| 00:00:01 |
| 2
| TABLE ACCESS
FULL | EMP
| 14
| 448
|
3 (0)| 00:00:01 |
-----------------------------------------------------------------------------
Note
-----
- dynamic sampling used for
this statement
通过比较发现,两个语句的成本cost会差出很多14vs4。所以,如果我们以后有上面类似的需求的话,可以考虑使用rollup。
注:rollup语法
select
a,b,组函数
from 表
group by
rollup(a,b);
这个语法相当于 group by a,b union group a union group by
null的sql语句的组合
Oracle SQL篇(四)group by 分组与分组的加强 rollup的更多相关文章
- Oracle SQL篇(一)null值之初体验
从我第一次正式的写sql语句到现在,已经超过10年的时间了.我写报表,做统计分析和财务对账,我一点点的接触oracle数据库,并尝试深入了解.这条路,一走就是10年,从充满热情,到开始厌 ...
- Oracle SQL篇(三)Oracle ROWNUM 与TOP N分析
首先我们来看一下ROWNUM: 含义解释: 1.rownum是oracle为从查询返回的行的编号,返回的第一行分配的是1,第二行是2,依此类推.这是一个伪列,可以用于限制查询返回的总行数. 2 ...
- Oracle SQL篇(二)oracle自连接操作
oracle 的自连接(self join)操作 对于oracle数据库来说,目前支持两套语法,一套是oracle自己的sql语法,一套是通行标准的SQL99语法,那么对于oracle的连接操 ...
- oracle学习篇五:组函数,分组统计
常用组函数: 1.ccount() 求出全部记录数. 2.max() 求出一组最大值 3.min() 求出一组最小值 4.avg() 求出平均值 5.sum() 求和 --1.统计员工数量: sele ...
- oracle学习篇四:多表查询
-----------------产生笛卡儿积------------------------------------ select * from emp,dept; --不带条件时,记录数为14*4 ...
- oracle sql 高级编程 历史笔记整理
20130909 周一 oracle sql 开发指南 第7章 高级查询 1.层次化查询select level,ttt.*,sys_connect_by_path(ttt.col1,',') fro ...
- 【基于WPF+OneNote+Oracle的中文图片识别系统阶段总结】之篇四:关于OneNote入库处理以及审核
篇一:WPF常用知识以及本项目设计总结:http://www.cnblogs.com/baiboy/p/wpf.html 篇二:基于OneNote难点突破和批量识别:http://www.cnblog ...
- 转:sql篇 select from where group by having order by
原文地址: sql篇 select from where group by having order by select from where group by having order by 的基 ...
- mongoDB 分组并对分组结果筛选类似于SQL中的(group by xxx having ) 附带Java代码
今天需要做一个筛选程序,因为数据放在mongodb中,没写过分组的查询语句,查了一些资料,终于写出来了,分享给各位小伙伴 需求是 查询 学员 在2019-07-29之后未同步的数据(同一个学员需要2条 ...
随机推荐
- asp.net Listbox控件用法
2008-02-18 19:56 来源: 作者: ListBox(列表框)控件可以显示一组项目的列表,用户可以根据需要从中选择一个或多个选项.列表框可以为用户提供所有选项的列表.虽然也可设置列表框为多 ...
- js中字符串方法
字符串方法: 1. charAt(索引值)//通过索引值获取字符串中对应的值 例如: var str='sdf123'; alert(str.charAt(0));//结果弹出第一个索引对应的值:s
- Replace不区分大小写
private string ReplaceNoCase(string text, string oldValue, string newValue) { return System.Text.Reg ...
- HTML与CSS绘制简单DIV布局
HTML代码<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF ...
- Oracle 查询时间在当天的数据
要实现这个功能需要用到trunc这个函数对时间的操作 select trunc(sysdate) from dual --2014-12-27 今天的日期为2014-12-27 select trun ...
- Android Gradle配置
解决问题 错误: Could not find the AndroidManifest.xml file, going up from path //打开app build.gradle文件加入以下代 ...
- Python正则表达式指南(转载)
转载自:http://www.cnblogs.com/huxi/archive/2010/07/04/1771073.html#3353540 1. 正则表达式基础 1.1. 简单介绍 正则表达式并不 ...
- 如何实现win7和VirtualBox中Ubuntu系统共享文件夹
设备: 1.win7 旗舰版 2.VirtualBox虚拟机 3.Ubuntu12.04 以前在VM虚拟机中可以直接进行复制就可以将win7系统的文件复制到虚拟机中,然后现在安装了Virt ...
- 在输出视图中使用使用html注释会导致在Chrome中css失效
在做SportsStore例子时,在视图List.cshtml中使用了html注释,即 <!-- 注释 --> 结果在加载css时,chrome浏览器中所有css效果都失效.IE不受影响. ...
- VS2015如何另存解决方案文件-修改解决方案sln文件的路径
原文:VS2005如何另存解决方案文件-修改解决方案sln文件的路径 修改解决方案sln文件的路径 方法一:工具→选项→项目和解决方案,可设置项目的默认保存位置.方法二:"解决方案资源管理器 ...