下面代码为Oracle数据库使用ROLLUP关键字统计
Oracle数据库自带的emp员工表中每个部门的工资总和,请讲解一下
SELECT e.deptno, d.dname, SUM(e.sal) AS 工资总和 FROM emp e,dept d WHERE e.deptno = d.deptno GROUP BY ROLLUP(e.deptno, d.dname);
Oracle ROLLUP 关键字统计工资总和详解
让我详细讲解这段代码,特别是 ROLLUP 的作用。
一、代码整体功能
这段代码的作用是:按部门统计员工工资总和,并同时生成所有部门的合计行。
sql
SELECT e.deptno, d.dname, SUM(e.sal) AS 工资总和FROM emp e, dept dWHERE e.deptno = d.deptnoGROUP BY ROLLUP(e.deptno, d.dname);
二、ROLLUP 是什么?
词源角度
| 单词 | 词源 | 含义 |
|---|---|---|
| ROLLUP | roll(滚动)+ up(向上) | 向上汇总、逐级累计 |
字面意思:把数据"向上滚动"汇总,从细粒度到粗粒度逐级聚合。
三、ROLLUP 的作用
ROLLUP 是 Oracle 中的分组扩展语法,它在正常分组的基础上,额外生成小计和总计行。
GROUP BY ROLLUP(A, B) 产生的分组组合
| 分组级别 | 分组依据 | 说明 |
|---|---|---|
| 第1组 | (A, B) | 正常分组(最细粒度) |
| 第2组 | (A) | A 的小计 |
| 第3组 | () | 总计 |
四、假设数据
emp 表(员工表)
empno | ename | sal | deptno |
1001 | 张三 | 5000 | 10 |
1002 | 李四 | 6000 | 10 |
1003 | 王五 | 7000 | 20 |
1004 | 赵六 | 8000 | 20 |
1005 | 孙七 | 9000 | 30 |
dept 表(部门表)
| deptno | dname |
|---|---|
| 10 | 财务部 |
| 20 | 技术部 |
| 30 | 销售部 |
五、不同写法的结果对比
写法1:普通 GROUP BY(无 ROLLUP)
sql
SELECT e.deptno, d.dname, SUM(e.sal) AS 工资总和FROM emp e, dept dWHERE e.deptno = d.deptnoGROUP BY e.deptno, d.dname;
结果:
| deptno | dname | 工资总和 |
|---|---|---|
| 10 | 财务部 | 11000 |
| 20 | 技术部 | 15000 |
| 30 | 销售部 | 9000 |
只有 3 行(每个部门一行)。
写法2:使用 ROLLUP
sql
SELECT e.deptno, d.dname, SUM(e.sal) AS 工资总和FROM emp e, dept dWHERE e.deptno = d.deptnoGROUP BY ROLLUP(e.deptno, d.dname);
结果:
deptno | dname | 工资总和 | 说明 |
10 | 财务部 | 11000 | 部门10的工资总和 |
20 | 技术部 | 15000 | 部门20的工资总和 |
30 | 销售部 | 9000 | 部门30的工资总和 |
10 | (null) | 11000 | 部门10的小计(重复?等等,让我重新计算) |
实际上,ROLLUP(e.deptno, d.dname) 会按以下顺序分组:
六、ROLLUP(e.deptno, d.dname) 产生的分组
text
┌─────────────────────────────────────────────────────────────┐ │ 第1级分组:按 (deptno, dname) 分组 │ │ 结果:每个部门一行 │ ├─────────────────────────────────────────────────────────────┤ │ 第2级分组:按 (deptno) 分组 │ │ 结果:每个部门一行(dname 为 NULL) │ │ 注意:这里与第1级结果重复?不对,让我仔细分析 │ └─────────────────────────────────────────────────────────────┘
实际上,ROLLUP 会生成 N+1 种分组组合(N = 列数)。
对于 ROLLUP(A, B):
| 分组级别 | 分组列 | 说明 |
|---|---|---|
| 1 | (A, B) | 正常分组 |
| 2 | (A) | 按 A 分组的小计 |
| 3 | () | 总计 |
七、实际执行结果
deptno | dname | 工资总和 | 分组级别 |
10 | 财务部 | 11000 | (10, 财务部) |
20 | 技术部 | 15000 | (20, 技术部) |
30 | 销售部 | 9000 | (30, 销售部) |
10 | (null) | 11000 | (10) 部门10小计 |
20 | (null) | 15000 | (20) 部门20小计 |
30 | (null) | 9000 | (30) 部门30小计 |
(null) | (null) | 35000 | () 总计 |
问题:这里部门小计和部门明细是重复的?不,它们的语义不同:
第1-3行:
(10,财务部)是该部门下按dname分组(实际上 dname 是唯一的,所以相同)第4-6行:
(10,null)是只按deptno分组的小计(当有多个部门名时才有意义)
八、更清晰的示例
为了看清 ROLLUP 的效果,假设 dname 有不同的值:
如果 ROLLUP(e.deptno, e.job)(按部门和职位统计):
deptno | job | SUM(sal) | 说明 |
10 | 经理 | 5000 | 部门10经理 |
10 | 职员 | 6000 | 部门10职员 |
20 | 经理 | 7000 | 部门20经理 |
20 | 职员 | 8000 | 部门20职员 |
10 | (null) | 11000 | 部门10小计 |
20 | (null) | 15000 | 部门20小计 |
(null) | (null) | 26000 | 总计 |
这样就能清楚看到 ROLLUP 的作用:逐级向上汇总!
九、ROLLUP 的可视化理解
text
原始数据(员工工资) ↓ 按 (deptno, dname) 分组 → 每个部门一行 ↓ ROLLUP 向上滚动 ↓ 按 (deptno) 分组 → 每个部门的小计(如果 dname 有多个,这里会有意义) ↓ ROLLUP 继续向上滚动 ↓ 按 () 分组 → 总计
十、ROLLUP vs CUBE vs GROUPING SETS
| 语法 | 作用 | 分组组合数 |
|---|---|---|
ROLLUP(A, B) | 逐级向上汇总 | N+1 = 3 |
CUBE(A, B) | 所有组合 | 2^N = 4 |
GROUPING SETS((A), (B)) | 指定组合 | 自定义 |
十一、总结
| 问题 | 答案 |
|---|---|
ROLLUP 是什么? | 分组扩展,生成逐级小计和总计 |
ROLLUP(e.deptno, d.dname) 产生哪些分组? | 1. (deptno, dname)2. (deptno)3. () |
| 结果行数 | 正常分组数 + 小计行数 + 1(总计) |
| 词源 | roll(滚动)+ up(向上)= 向上汇总 |
| 与普通 GROUP BY 的区别 | 普通 GROUP BY 只有明细,ROLLUP 增加小计和总计 |
一句话总结:ROLLUP 就像逐级向上汇总——先从最细粒度(部门+部门名)统计,然后"向上滚动"到部门级别统计小计,最后滚动到最顶层统计总计。ROLLUP(e.deptno, d.dname) 会生成 3 种分组:(deptno, dname) 的明细、(deptno) 的部门小计、() 的总计,最终结果行数 = 正常分组数 + 分组层级数