理解group by 语句的扩展使用

在SQL的开发中我们会经常使用group by语句对数据进行分组统计,然而在一些复杂的BI报表开发中会常遇到更
复杂的分组需求,单单使用group by 就不能解决我们的问题了,这时我们就需要学习了解一下在group by 语句上的一些扩展使用,下面我们就来学习一下rollup、cube、grouping sets语句的使用。

1.group by

SQL> select a.dname,b.job,sum(b.sal) sum_sal
  2  from dept a,emp b
  3  where a.deptno=b.deptno
  4  group by a.dname,b.job;
 
DNAME          JOB          SUM_SAL
-------------- --------- ----------
SALES          MANAGER         2850
SALES          CLERK            950
ACCOUNTING     MANAGER         2450
ACCOUNTING     PRESIDENT       5000
ACCOUNTING     CLERK           1300
RESEARCH       MANAGER         2975
SALES          SALESMAN        5600
RESEARCH       ANALYST         6000
RESEARCH       CLERK           1900
 
9 rows selected

可以看到以上数据中我们按照部门名和职位名进行了分组,然后求出每组内的工资数。
假如现在我们有一个需求,需要在这个查询中增加一个不同部门间的工资总和,那么这个SQL该怎么写:

我们是不是会这样写:

SQL> select * from (
  2  SELECT  a.dname,b.job,SUM(b.sal) sum_sal
  3  FROM dept a,emp b
  4  WHERE a.deptno = b.deptno
  5  GROUP  BY a.dname,b.job
  6  UNION ALL
  7  --部门的小计
  8  SELECT  a.dname,NULL, SUM(b.sal) sum_sal
  9  FROM dept a,emp b
 10  WHERE a.deptno = b.deptno
 11  GROUP  BY a.dname
 12  UNION ALL
 13  --所有部门总的合计
 14  SELECT  NULL,NULL, SUM(b.sal) sum_sal
 15  FROM dept a,emp b
 16  WHERE a.deptno = b.deptno)
 17  order by dname;
 
DNAME          JOB          SUM_SAL
-------------- --------- ----------
ACCOUNTING                     8750
ACCOUNTING     MANAGER         2450
ACCOUNTING     PRESIDENT       5000
ACCOUNTING     CLERK           1300
RESEARCH       CLERK           1900
RESEARCH       MANAGER         2975
RESEARCH                      10875
RESEARCH       ANALYST         6000
SALES          CLERK            950
SALES          MANAGER         2850
SALES          SALESMAN        5600
SALES                          9400
                              29025
 
13 rows selected

可以看到上面的SQL写法实现了之前的需求,但是执行效率将非常低,dept表和emp表将会被多次扫描,
能否一次扫描就能搞定这个事哪,答案的肯定的,使用rollup函数。

2.group by rollup

SQL> select a.dname,b.job,sum(b.sal) sum_sal
  2  from dept a,emp b
  3  where a.deptno=b.deptno
  4  group by rollup(a.dname,b.job);
 
DNAME          JOB          SUM_SAL
-------------- --------- ----------
SALES          CLERK            950
SALES          MANAGER         2850
SALES          SALESMAN        5600
SALES                          9400
RESEARCH       CLERK           1900
RESEARCH       ANALYST         6000
RESEARCH       MANAGER         2975
RESEARCH                      10875
ACCOUNTING     CLERK           1300
ACCOUNTING     MANAGER         2450
ACCOUNTING     PRESIDENT       5000
ACCOUNTING                     8750
                              29025
 
13 rows selected

通过上面的查询可以看到使用rollup函数我们一次就实现了上面的需求,而且dept表和emp表将只会被扫描一次,可以通过查询执行计划要来验证(略)。

group by rollup(a.dname,b.job)
分组是这样的:
1.首先对a.dname,b.job进行联合分组,求出sum
2.其次对a.dname进行分组,求出sum
3.最后按全表进行分组求出sum

可能有些人对于上面union all的写法还能接受,决定实现起来不难而且很好理解,但是我要说的是如果是需求再改变,在增加求出对雇佣年份的统计,是不是又要union all了,这就显得sql很冗肿了,效率下降,你还能接受吗?如果现在我们的需求又改变了,我们(a.dname,b.job)在前面的基础上增加对 b.job进行分组求和,这时该怎么做那,。。。。这时就需要我们使用cube函数。

3.group by cube

SQL> select a.dname,b.job,sum(b.sal) sum_sal
  2  from dept a,emp b
  3  where a.deptno=b.deptno
  4  group by cube(a.dname,b.job);
 
DNAME          JOB          SUM_SAL
-------------- --------- ----------
                              29025
               CLERK           4150
               ANALYST         6000
               MANAGER         8275
               SALESMAN        5600
               PRESIDENT       5000
SALES                          9400
SALES          CLERK            950
SALES          MANAGER         2850
SALES          SALESMAN        5600
RESEARCH                      10875
RESEARCH       CLERK           1900
RESEARCH       ANALYST         6000
RESEARCH       MANAGER         2975
ACCOUNTING                     8750
ACCOUNTING     CLERK           1300
ACCOUNTING     MANAGER         2450
ACCOUNTING     PRESIDENT       5000
 
18 rows selected


通过上面的sql查询我们发现cube函数是rollup函数基础上更细化的分组,在rollup的基础上又增加了对job的分组,是不是这样的那??? 通过上面的查询发现使用rollup函数有13条数据,相同数据的情况下使用cube函数有18条数据,那么多出的5条数据就是对job的分组,查询一下job有种:

SQL> select distinct job from emp;
 
JOB
---------
CLERK
SALESMAN
PRESIDENT
MANAGER
ANALYST

可以看到正好有5种job,验证了上面的问题。

group by cube(a.dname,b.job)
分组是这样的:
1.首先按照a.dname,b.job进行分组,求聚合函数的值
2.其次按照a.dname进行分组,求聚合函数的值
3.再次按照b.job进行分组,求聚合函数的值
4.最后对全表进行分组,求聚合函数的值

如果是三列数据那:

group by cube(a.dname,b.job,b.hiredate)

分组是这样的:
1.首先按照a.dname,b.job,b.hiredate进行分组,求聚合函数的值
2.然后按照a.dname,b.job进行分组,求聚合函数的值
3.然后按照a.dname,b.hiredate进行分组,求聚合函数的值
4.然后按照b.job,b.hiredate进行分组,求聚合函数的值
5.然后按照a.dname进行分组,求聚合函数的值
6.然后按照b.job进行分组,求聚合函数的值
7.然后按照b.hiredate进行分组,求聚合函数的值
8.最后按照全表进行分组,求聚合函数的值

例如:

SQL> select a.dname,b.job,b.hiredate,sum(b.sal) sum_sal
  2  from dept a,emp b
  3  where a.deptno=b.deptno
  4  group by cube(a.dname,b.job,b.hiredate);
 
DNAME          JOB       HIREDATE       SUM_SAL
-------------- --------- ----------- ----------
                                          29025
                         1980/12/17         800
                         1981/2/20         1600
                         1981/2/22         1250
                         1981/4/2          2975
                         1981/5/1          2850
                         1981/6/9          2450
                         1981/9/8          1500
                         1981/9/28         1250
                         1981/11/17        5000
                         1981/12/3         3950
                         1982/1/23         1300
                         1987/4/19         3000
                         1987/5/23         1100
               CLERK                       4150
               CLERK     1980/12/17         800
               CLERK     1981/12/3          950
               CLERK     1982/1/23         1300
               CLERK     1987/5/23         1100
               ANALYST                     6000
               ANALYST   1981/12/3         3000
               ANALYST   1987/4/19         3000
               MANAGER                     8275
               MANAGER   1981/4/2          2975
               MANAGER   1981/5/1          2850
               MANAGER   1981/6/9          2450
               SALESMAN                    5600
               SALESMAN  1981/2/20         1600
               SALESMAN  1981/2/22         1250
               SALESMAN  1981/9/8          1500
               SALESMAN  1981/9/28         1250
               PRESIDENT                   5000
               PRESIDENT 1981/11/17        5000
SALES                                      9400
SALES                    1981/2/20         1600
SALES                    1981/2/22         1250
SALES                    1981/5/1          2850
SALES                    1981/9/8          1500
SALES                    1981/9/28         1250
SALES                    1981/12/3          950
SALES          CLERK                        950
SALES          CLERK     1981/12/3          950
SALES          MANAGER                     2850
SALES          MANAGER   1981/5/1          2850
SALES          SALESMAN                    5600
SALES          SALESMAN  1981/2/20         1600
SALES          SALESMAN  1981/2/22         1250
SALES          SALESMAN  1981/9/8          1500
SALES          SALESMAN  1981/9/28         1250
RESEARCH                                  10875
RESEARCH                 1980/12/17         800
RESEARCH                 1981/4/2          2975
RESEARCH                 1981/12/3         3000
RESEARCH                 1987/4/19         3000
RESEARCH                 1987/5/23         1100
RESEARCH       CLERK                       1900
RESEARCH       CLERK     1980/12/17         800
RESEARCH       CLERK     1987/5/23         1100
RESEARCH       ANALYST                     6000
RESEARCH       ANALYST   1981/12/3         3000
RESEARCH       ANALYST   1987/4/19         3000
RESEARCH       MANAGER                     2975
RESEARCH       MANAGER   1981/4/2          2975
ACCOUNTING                                 8750
ACCOUNTING               1981/6/9          2450
ACCOUNTING               1981/11/17        5000
ACCOUNTING               1982/1/23         1300
ACCOUNTING     CLERK                       1300
ACCOUNTING     CLERK     1982/1/23         1300
ACCOUNTING     MANAGER                     2450
ACCOUNTING     MANAGER   1981/6/9          2450
ACCOUNTING     PRESIDENT                   5000
ACCOUNTING     PRESIDENT 1981/11/17        5000
 
73 rows selected

如果分别按照a.dname,b.job,b.hiredate进行分组求和,我们是不是可以放在一个SQL中实现,
答案的可以的,我们可以利用grouping sets函数

4.group by grouping sets

SQL> select a.dname,b.job,b.hiredate,sum(b.sal) sum_sal
  2  from dept a,emp b
  3  where a.deptno=b.deptno
  4  group by grouping sets (a.dname,b.job,b.hiredate);
 
DNAME          JOB       HIREDATE       SUM_SAL
-------------- --------- ----------- ----------
                         1987/5/23         1100
                         1981/11/17        5000
                         1982/1/23         1300
                         1981/12/3         3950
                         1981/2/22         1250
                         1981/6/9          2450
                         1980/12/17         800
                         1981/4/2          2975
                         1987/4/19         3000
                         1981/2/20         1600
                         1981/9/8          1500
                         1981/5/1          2850
                         1981/9/28         1250
               CLERK                       4150
               SALESMAN                    5600
               PRESIDENT                   5000
               MANAGER                     8275
               ANALYST                     6000
ACCOUNTING                                 8750
RESEARCH                                  10875
SALES                                      9400
 
21 rows selected

通过上面的查询我们就很一目了然看到他们的分组规律。

group by grouping sets (a.dname,b.job,b.hiredate)
分组是这样的:
1.首先按照a.dname进行分组,求聚合函数的值
2.其次按照b.job进行分组,求聚合函数的值
1.最后按照b.hiredate进行分组,求聚合函数的值

5.总结归类下

Rollup是在group by的基础上再进行分级的汇总,例如:Rollup(A,B,C)的分组顺序是:
(A,B,C)
(A,B)
(A)
最后对全表进行group by 分组。

Cube是在Rollup的基础上再进行更加细粒度的汇总,例如:cube(A,B,C)它的分组顺序是:
(A,B,C)
(A,B)
(A,C)
(A)
(B,C)
(B)
(C)
最后对全表进行group by 分组。

Grouping sets与rollup和cube不同,它只是对单列进行分组,例如grouping sets(A,B,C)的分组顺序是:
(A)
(B)
(C)

可参考:

http://love-flying-snow.iteye.com/blog/573083

原文地址:https://www.cnblogs.com/myrunning/p/4344742.html