ROLLUP
rollup相對於簡單的分組合計增加了小計和合計(適用於統計功能),解釋起來會比較抽象,下面我們來看看具體事例。
1.統計不同部門工資的總和和所有部門工資的總和。
select deptno,sum(sal) from emp group by deptno with rollup;
2.先對deptno進行分組,再對job進行分組
select deptno,job,sum(sal) from emp group by deptno,job with rollup;
這里可能就有人會有疑問了,為什么數據比原來多了10條,理論上應該只是全部的總結,只會比group by 多一條數據才合理,查了資料才弄明白,對於聚合單數據(group by 后面的變量一個為單數據,多個則為多數據)是適合的,但是在多數據的情況下就不一樣了,下面我們分析一下,首先會根據depno變量,將原始數據分為9個101,102,103...109九個組,group by WITH ROLLUP會在每個分組后面加上本組類的信息(deptno),第5行數據就是1,2,3,4行數據聚合所執行sum(sal)所得的結果,依次類推...108,109也是一樣,同時在最后,會將全部的分組聚合。
性能分析:
3.對比不使用 rollup實現類似功能(可用UNION ALL語句)
#實現單個部門,單個工種的工資的總和 select deptno,job,sum(sal) from emp group by deptno,job union all #實現單個部門工資的總和 select deptno,null,sum(sal) from emp group by deptno union all #實現所有部門工資的總和 select null,null,sum(sal) from emp order by deptno desc,job desc
性能分析:
總結:
通過2,3執行結果的性能分析:不難看出,相同的功能實現,ROLLUP相對於UNION ALL效率有了極大的提升。
實戰案例:
#emp 表結構 CREATE TABLE `emp` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `empno` mediumint(8) unsigned NOT NULL DEFAULT '0', `ename` varchar(20) NOT NULL DEFAULT '', `job` varchar(9) NOT NULL DEFAULT '', `mgr` mediumint(8) unsigned NOT NULL DEFAULT '0', `hiredate` date NOT NULL, `sal` decimal(7,2) NOT NULL, `comm` decimal(7,2) NOT NULL, `deptno` mediumint(8) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=500001 DEFAULT CHARSET=utf8;
1.對員工表的信息按照部門,匯總崗位信息的薪資情況。
#常規處理 select deptno,sum(if(job = 'dev',sal,null)) dev_sal,sum(if(job = 'manager',sal,null)) manager_sal,sum(if(job = 'salesman',sal,null)) salesman_sal,sum(if(job = 'Test',sal,null)) Test_sal from emp group by deptno
2.如果相對上結果,加上一個匯總展示,如何處理?
#1.使用 union all select deptno,sum(if(job = 'dev',sal,null)) dev_sal,sum(if(job = 'manager',sal,null)) manager_sal,sum(if(job = 'salesman',sal,null)) salesman_sal,sum(if(job = 'Test',sal,null)) Test_sal from emp group by deptno union all select null ,sum(if(job = 'dev',sal,null)) dev_sal,sum(if(job = 'manager',sal,null)) manager_sal,sum(if(job = 'salesman',sal,null)) salesman_sal,sum(if(job = 'Test',sal,null)) Test_sal from emp #2.使用 rollup select deptno,sum(if(job = 'dev',sal,null)) dev_sal,sum(if(job = 'manager',sal,null)) manager_sal,sum(if(job = 'salesman',sal,null)) salesman_sal,sum(if(job = 'Test',sal,null)) Test_sal from emp group by deptno with rollup;
存在問題:
1. 這個記錄沒有出現總計兩個字,怎么實現呢? 使用 ifnull()函數 (有坑)
#有坑版 select ifnull(deptno,'匯總') deptno,sum(if(job = 'dev',sal,null)) dev_sal,sum(if(job = 'manager',sal,null)) manager_sal,sum(if(job = 'salesman',sal,null)) salesman_sal,sum(if(job = 'Test',sal,null)) Test_sal from emp group by deptno with rollup; #為什么說有坑? 如果deptno 本身為null ,那么以上查詢將會出現多個匯總行。 #填坑版 (使用雙層ifnull判斷 or 將字段定義為not null) select ifnull(deptno,'匯總') deptno,dev_sal,manager_sal,salesman_sal,Test_sal from ( select ifnull(deptno,'空城(默認)') deptno,sum(if(job = 'dev',sal,null)) dev_sal,sum(if(job = 'manager',sal,null)) manager_sal,sum(if(job = 'salesman',sal,null)) salesman_sal,sum(if(job = 'Test',sal,null)) Test_sal from emp group by deptno ) a group by deptno with rollup;
2.如果想對其中一列進行排序如何處理?使用order by field()處理
#天真的處理方式 select ifnull(deptno,'空城(默認)') deptno,sum(if(job = 'dev',sal,null)) dev_sal,sum(if(job = 'manager',sal,null)) manager_sal,sum(if(job = 'salesman',sal,null)) salesman_sal,sum(if(job = 'Test',sal,null)) Test_sal from emp group by deptno with rollup ORDER BY Test_sal ; #結果報錯: Incorrect usage of CUBE/ROLLUP and ORDER BY #原因:MySQL 雖然提供了 group by with rollup 函數進行group by 字段的匯總,but 與 order by 互斥,不能同時用 #正確方式 使用order by field() #根據測試崗位的Test_sal薪資降序排列 總計置於底部 =>可以把上面代碼當成一個子表嵌套 結合 order by field()自定義函數實現 select *from ( select ifnull(deptno,'總計') deptno,dev_sal,manager_sal,salesman_sal,Test_sal from ( select ifnull(deptno,'空城(默認)') deptno,sum(if(job = 'dev',sal,null)) dev_sal, sum(if(job = 'manager',sal,null)) manager_sal,sum(if(job = 'salesman',sal,null)) salesman_sal, sum(if(job = 'Test',sal,null)) Test_sal from emp group by deptno )a group by deptno with rollup ) b ORDER BY FIELD(b.deptno,'總計'),b.Test_sal DESC;