分组操作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相关推荐

  1. 力扣刷题学习SQL篇——1-8 查询(按日期分组销售产品——利用聚合函数GROUP_CONCAT)

    力扣刷题学习SQL篇--1-8 查询(按日期分组销售产品--利用聚合函数GROUP_CONCAT) 1.题目 2.解法 3.group_concat() 1.题目 题目链接:https://leetc ...

  2. 玩转SQL语句之group by 多字段分组查询与having子句,一篇解决你的疑惑!

    sql语句group by使用详解 group by的基本语法 基本语法 什么是分组查询(一个字段) 多个字段的分组查询 1.两个字段的分组查询 2.三个字段及N个字段进行分组查询 having子句的 ...

  3. Oracle SQL篇(三)Oracle ROWNUM 与TOP N分析

        首先我们来看一下ROWNUM: 含义解释: 1.rownum是oracle为从查询返回的行的编号,返回的第一行分配的是1,第二行是2,依此类推.这是一个伪列,可以用于限制查询返回的总行数. 2 ...

  4. oracle 汉字正则表达式,oracle SQL篇-正则表达式

    常见函数: REGEXP_LIKE LIKE语句的正则表达式, REGEXP_REPLACE 用执行字符串替换源文本中与正则表达式匹配的字符串, REGEXP_INSTR返回源字符串中首次匹配正则表达 ...

  5. c#扩展方法奇思妙用高级篇四:对扩展进行分组管理

    从系列文章开篇到现在,已经实现的很多扩展了,但过多的扩展会给我们带来很多麻烦,试看下图: 面对这么多"泛滥"的扩展,很多人都会感到很别扭,的确有种"喧宾夺主"的 ...

  6. 解决数据倾斜一:RDD执行reduceByKey或则Spark SQL中使用group by语句导致的数据倾斜

    一:概述 有的时候,我们可能会遇到大数据计算中一个最棘手的问题--数据倾斜,此时Spark作业的性能会比期望差很多.数据倾斜调优,就是使用各种技术方案解决不同类型的数据倾斜问题,以保证Spark作业的 ...

  7. [强烈推荐]ORACLE SQL:经典查询练手第四篇(不懂装懂,永世饭桶!)

    [推荐]ORACLE SQL: 经典查询练手第四篇(不懂装懂,永世饭桶!) --通过知识共享树立个人品牌. 本文与大家共同讨论与分享ORACLE SQL的一些常用经典查询,欢迎大家补充,同时你认为有那 ...

  8. oracle sql 平均分配 分组_SQL学习二

    训练大纲(第023天) 大家如果想快速有效的学习,思想核心是"以建立知识体系为核心",具体方法是"守破离".确保老师课堂上做的操作,反复练习直到熟练. 第41次 ...

  9. [推荐推荐][提供下载]ORACLE SQL:经典查询练手系列文章收尾(目录篇)

    [推荐推荐][提供下载]ORACLE SQL: 经典查询练手系列文章收尾(目录篇) --通过知识共享树立个人品牌. 通过近一个月的努力,<经典查询练手系列>也快告一段落,但并不代表结束,以 ...

  10. oracle group by sql,Oracle SQL GROUP BY“不是GROUP BY表达式”的帮助

    我有一张table some_table +--------+----------+---------------------+-------+ | id | other_id | date_valu ...

最新文章

  1. C错误异常处理,异常处理
  2. push_back()和emplace_back()函数
  3. LibLinear(SVM包)使用说明之(一)README
  4. linux的线程实验的实验结果,Linux线程qps测试
  5. git diff命令详解
  6. std::vectorChannel2* m_allChannels;容器,以及如何根据channelid的意义
  7. h5页面保存img_一文彻底解决HTML5页面中长按保存图片功能
  8. IAM(身份验证以及访问控制)
  9. Google开源新AI模型,语音区分准确率92%创新高 | 论文+GitHub
  10. JupterNoteBook
  11. 国内经典BI系统架构分析
  12. 凤凰项目:一个IT运维的传奇故事
  13. Ubuntu20.04显卡驱动安装
  14. Win10清理C盘垃圾
  15. OKR测试试卷模板(进阶)
  16. excel页码怎么设置从4开始?
  17. 镰仓物语 | 亲近的人即便离开人间,它也在你的身边
  18. 虚拟机挂载优盘和识别优盘
  19. 宏碁掠夺者Predator首款RGB内存条即将发售,特挑三星B-Die颗粒
  20. 域渗透基础_域渗透实战下gpo策略利用

热门文章

  1. Ubuntu Java环境配置
  2. 更多编译器对C++11的支持比较
  3. (转)重写重载多态,接口与类
  4. c#解析XML到DATASET及dataset转为xml文件函数
  5. 选择排序法之Java实现
  6. poj 3261 Milk Patterns
  7. 实验3: DHCP 基本配置
  8. 【HDU-5246】超级赛亚ACMer(贪心)
  9. linux c 笔记-1
  10. 利用 SendInput 和INPUT结构 模拟鼠标移动