你前一个生成的结果中deptno列和dname的列中都有null的,
这里怎么没有了
您观察得非常仔细!完全正确!
ROLLUP 会生成小计和总计行,
那些行中的 deptno 和 dname 会是 NULL。
一、代码回顾
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);
关键:这里有 ROLLUP!不是普通的 GROUP BY。
二、ROLLUP(e.deptno, d.dname) 产生的分组
ROLLUP 会生成 3 种分组级别:
级别 | 分组依据 | 说明 | deptno | dname |
1 | (deptno, dname) | 正常分组(最细粒度) | 有值 | 有值 |
2 | (deptno) | 按部门小计 | 有值 | NULL |
3 | () | 总计 | NULL | NULL |
三、正确的最终结果
假设数据(与之前相同)
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 | 销售部 |
正确结果
deptno | dname | 工资总和 | 分组级别 |
10 | 财务部 | 11000 | 级别1:(10, 财务部) |
20 | 技术部 | 15000 | 级别1:(20, 技术部) |
30 | 销售部 | 9000 | 级别1:(30, 销售部) |
10 | NULL | 11000 | 级别2:(10) 部门10小计 |
20 | NULL | 15000 | 级别2:(20) 部门20小计 |
30 | NULL | 9000 | 级别2:(30) 部门30小计 |
NULL | NULL | 35000 | 级别3:() 总计 |
四、可视化理解
text
┌─────────────────────────────────────────────────────────────┐ │ 级别1:按 (deptno, dname) 分组 │ ├─────────────────────────────────────────────────────────────┤ │ │ deptno │ dname │ 工资总和 │ │ │ │ 10 │ 财务部 │ 11000 │ │ │ │ 20 │ 技术部 │ 15000 │ │ │ │ 30 │ 销售部 │ 9000 │ │ └─────────────────────────────────────────────────────────────┘ ↓ ROLLUP 向上滚动 ↓ ┌─────────────────────────────────────────────────────────────┐ │ 级别2:按 (deptno) 分组(小计) │ ├─────────────────────────────────────────────────────────────┤ │ │ deptno │ dname │ 工资总和 │ │ │ │ 10 │ NULL │ 11000 │ ← 部门10的小计 │ │ │ 20 │ NULL │ 15000 │ ← 部门20的小计 │ │ │ 30 │ NULL │ 9000 │ ← 部门30的小计 │ └─────────────────────────────────────────────────────────────┘ ↓ ROLLUP 继续向上滚动 ↓ ┌─────────────────────────────────────────────────────────────┐ │ 级别3:按 () 分组(总计) │ ├─────────────────────────────────────────────────────────────┤ │ │ deptno │ dname │ 工资总和 │ │ │ │ NULL │ NULL │ 35000 │ ← 所有部门的总计 │ └─────────────────────────────────────────────────────────────┘
五、为什么会有 NULL?
分组级别 | deptno | dname | 为什么是 NULL |
级别1 | 有值 | 有值 | 正常分组,都有值 |
级别2 | 有值 | NULL | 只按 deptno 分组,dname 不参与分组,所以为 NULL |
级别3 | NULL | NULL | 总计行,不按任何列分组,所以都为 NULL |
六、ROLLUP vs 普通 GROUP BY 对比
写法 | 结果行数 | 是否有 NULL | 是否有小计/总计 |
GROUP BY e.deptno, d.dname | 3行 | ❌ 无 | ❌ 无 |
GROUP BY ROLLUP(e.deptno, d.dname) | 7行 | ✅ 有 | ✅ 有 |
七、如何区分明细行和小计/总计行?
Oracle 提供了 GROUPING() 函数来判断:
sql
SELECT e.deptno, d.dname, SUM(e.sal) AS 工资总和, GROUPING(e.deptno) AS 是否部门小计, GROUPING(d.dname) AS 是否总计FROM emp e, dept dWHERE e.deptno = d.deptnoGROUP BY ROLLUP(e.deptno, d.dname);
结果:
deptno | dname | 工资总和 | 是否部门小计 | 是否总计 |
10 | 财务部 | 11000 | 0 | 0 |
20 | 技术部 | 15000 | 0 | 0 |
30 | 销售部 | 9000 | 0 | 0 |
10 | NULL | 11000 | 1 | 0 |
20 | NULL | 15000 | 1 | 0 |
30 | NULL | 9000 | 1 | 0 |
NULL | NULL | 35000 | 1 | 1 |
GROUPING(...)=0:正常明细行GROUPING(...)=1:小计或总计行
八、总结
| 问题 | 答案 |
|---|---|
| 结果中会有 NULL 吗? | ✅ 会! 小计行和总计行的 dname 或 deptno 为 NULL |
| 为什么我之前的回答没有 NULL? | 我错误地给出了普通 GROUP BY 的结果,忽略了 ROLLUP |
| 正确的结果有几行? | 7行(3行明细 + 3行小计 + 1行总计) |
| NULL 代表什么? | 代表该列不参与当前级别的分组 |
一句话总结:使用 ROLLUP 时,结果中确实会出现 NULL——小计行的 dname 为 NULL,总计行的 deptno 和 dname 都为 NULL。