SQL Server GROUP BY中的WITH CUBE、WITH ROLLUP原理測試及GROUPING應用


    前幾天,看到一個群友用WITH ROLLUP運算符。由於自個兒沒用過,看到概念及結果都雲里霧里的,所以突然來了興趣對生成結果測了一番。

    一、概念:

    WITH CUBE:生成的結果集顯示了所選列中值的所有組合的聚合。

    WITH ROLLUP:生成的結果集顯示了所選列中值的某一層次結構的聚合。

    GROUPING:當行由 WITH CUBE或WITH ROLLUP運算符添加時,該函數將導致附加列的輸出值為 1;當行不由 CUBE 或 ROLLUP 運算符添加時,該函數將導致附加列的輸出值為 0。僅在與包含 CUBE 或 ROLLUP 運算符的 GROUP BY 子句相關聯的選擇列表中才允許分組。

    二、測試:

    1、建立臨時表

CREATE TABLE #T0
(
    [GRADE] [VARCHAR](50) NULL,     --年級
    [CLASS] [VARCHAR](50) NULL,     --班級
    [NAME] [VARCHAR](50) NULL,      --姓名
    [COURSE] [VARCHAR](50) NULL,    --學科
    [RESULT] [NUMERIC](8,2) NULL    --成績
)

CREATE TABLE #T1
(
    [ID] [INT] IDENTITY(1,1) NOT NULL,    --序號
    [GRADE] [VARCHAR](50) NULL,           --年級
    [CLASS] [VARCHAR](50) NULL,           --班級
    [NAME] [VARCHAR](50) NULL,            --姓名
    [COURSE] [VARCHAR](50) NULL,          --學科
    [RESULT] [NUMERIC](8,2) NULL          --成績
)

CREATE TABLE #T2
(
    [ID] [INT] IDENTITY(1,1) NOT NULL,    --序號
    [GRADE] [VARCHAR](50) NULL,           --年級
    [CLASS] [VARCHAR](50) NULL,           --班級
    [NAME] [VARCHAR](50) NULL,            --姓名
    [COURSE] [VARCHAR](50) NULL,          --學科
    [RESULT] [NUMERIC](8,2) NULL          --成績
)

     2、插入測試數據

INSERT INTO #T0 (GRADE,CLASS,NAME,COURSE,RESULT)
SELECT '2019','CLASS1','9A01','C#',100
UNION
SELECT '2019','CLASS1','9A02','C#',100
UNION
SELECT '2019','CLASS2','9B01','C#',100
UNION
SELECT '2019','CLASS2','9B02','C#',100
UNION
SELECT '2018','CLASS1','8A01','JAVA',100
UNION
SELECT '2018','CLASS1','8A02','JAVA',100
UNION
SELECT '2018','CLASS2','8B01','JAVA',100
UNION
SELECT '2018','CLASS2','8B02','JAVA',100

    查詢T0表結果:

    3、GROUP BY

    拋磚引玉,看看常用的GROUP BY排序:默認以SELECT字段順序(GRADE->CLASS->NAME->COURSE)進行排序,以下兩種查詢結果是一樣的。

SELECT GRADE,CLASS,NAME,COURSE,SUM(RESULT) RESULT
FROM #T0
GROUP BY GRADE,CLASS,NAME,COURSE

SELECT GRADE,CLASS,NAME,COURSE,SUM(RESULT) RESULT
FROM #T0
GROUP BY GRADE,CLASS,NAME,COURSE
ORDER BY GRADE,CLASS,NAME,COURSE

    4、WITH CUBE

    原理1:以GROUP BY字段依次賦以NULL值進行分組聚合。

    原理2:第1個字段(即GRADE字段)生成結果:除原始數據外,以第1個字段固定賦以NULL值,然后其它字段依次賦以NULL值進行分組聚合,結果由右往左進行排序

    下面開始測第1個字段的結果是怎么來的:

INSERT INTO #T1 (GRADE,CLASS,NAME,COURSE,RESULT)
SELECT GRADE,CLASS,NAME,COURSE,SUM(RESULT) RESULT 
FROM #T0 
GROUP BY GRADE,CLASS,NAME,COURSE

INSERT INTO #T1 (GRADE,CLASS,NAME,COURSE,RESULT)
SELECT 'ZZ' GRADE,CLASS,NAME,COURSE,SUM(RESULT) RESULT 
FROM #T0 
GROUP BY CLASS,NAME,COURSE

INSERT INTO #T1 (GRADE,CLASS,NAME,COURSE,RESULT)
SELECT 'ZZ' GRADE,'ZZ' CLASS,NAME,COURSE,SUM(RESULT) RESULT 
FROM #T0 
GROUP BY NAME,COURSE

INSERT INTO #T1 (GRADE,CLASS,NAME,COURSE,RESULT)
SELECT 'ZZ' GRADE,'ZZ' CLASS,'ZZ' NAME,COURSE,SUM(RESULT) RESULT 
FROM #T0 
GROUP BY COURSE

INSERT INTO #T1 (GRADE,CLASS,NAME,COURSE,RESULT)
SELECT 'ZZ' GRADE,'ZZ' CLASS,'ZZ' NAME,'ZZ' COURSE,SUM(RESULT) RESULT 
FROM #T0

--第1個字段結果排序由右往左
INSERT INTO #T2 (GRADE,CLASS,NAME,COURSE,RESULT)
SELECT GRADE,CLASS,NAME,COURSE,RESULT FROM #T1 WHERE ID BETWEEN 1 AND 27 ORDER BY COURSE,NAME,CLASS,GRADE

UPDATE #T2 SET GRADE=NULL WHERE GRADE='ZZ'
UPDATE #T2 SET CLASS=NULL WHERE CLASS='ZZ'
UPDATE #T2 SET NAME=NULL WHERE NAME='ZZ'
UPDATE #T2 SET COURSE=NULL WHERE COURSE='ZZ'

    WITH CUBE的結果:

SELECT GRADE,CLASS,NAME,COURSE,SUM(RESULT) RESULT
FROM #T0
GROUP BY GRADE,CLASS,NAME,COURSE
WITH CUBE

    自已測試的結果:

SELECT * FROM #T2

    結果與上面一致。

    其它字段優先跟哪個字段組合、最終怎樣排序?呃,測過,沒搞清楚……

    5、WITH ROLLUP

    原理1:除原始數據外,以GROUP BY最后1個字段(即COURSE字段)固定賦以NULL值,然后其它字段依次賦以NULL值進行分組聚合,結果由左往右進行排序

    這個跟WITH CUBE的第1個字段非常相象:一個是第1個字段,一個是最后1個字段;一個結果是由右往左排序,一個結果是由左往右排序。

    下面開始測結果是怎么來的:

TRUNCATE TABLE #T1
TRUNCATE TABLE #T2

INSERT INTO #T1 (GRADE,CLASS,NAME,COURSE,RESULT)
SELECT GRADE,CLASS,NAME,COURSE,SUM(RESULT) RESULT 
FROM #T0 
GROUP BY GRADE,CLASS,NAME,COURSE

INSERT INTO #T1 (GRADE,CLASS,NAME,COURSE,RESULT)
SELECT GRADE,CLASS,NAME,'ZZ' COURSE,SUM(RESULT) RESULT 
FROM #T0 
WHERE NOT EXISTS (SELECT 1 FROM #T1 WHERE GRADE=#T0.GRADE AND CLASS=#T0.GRADE AND NAME=#T0.NAME AND COURSE='ZZ')
GROUP BY GRADE,CLASS,NAME

INSERT INTO #T1 (GRADE,CLASS,NAME,COURSE,RESULT)
SELECT GRADE,CLASS,'ZZ' NAME,'ZZ' COURSE,SUM(RESULT) RESULT 
FROM #T0 
WHERE NOT EXISTS (SELECT 1 FROM #T1 WHERE GRADE=#T0.GRADE AND CLASS=#T0.CLASS AND NAME='ZZ' AND COURSE='ZZ')
GROUP BY GRADE,CLASS

INSERT INTO #T1 (GRADE,CLASS,NAME,COURSE,RESULT)
SELECT GRADE,'ZZ' CLASS,'ZZ' NAME,'ZZ' COURSE,SUM(RESULT) RESULT 
FROM #T0 
WHERE NOT EXISTS (SELECT 1 FROM #T1 WHERE GRADE=#T0.GRADE AND CLASS='ZZ' AND NAME='ZZ' AND COURSE='ZZ')
GROUP BY GRADE

INSERT INTO #T1 (GRADE,CLASS,NAME,COURSE,RESULT)
SELECT 'ZZ' GRADE,'ZZ' CLASS,'ZZ' NAME,'ZZ' COURSE,SUM(RESULT) RESULT 
FROM #T0 

--結果排序由左往右
INSERT INTO #T2 (GRADE,CLASS,NAME,COURSE,RESULT)
SELECT GRADE,CLASS,NAME,COURSE,RESULT FROM #T1 ORDER BY GRADE,CLASS,NAME,COURSE

UPDATE #T2 SET GRADE=NULL WHERE GRADE='ZZ'
UPDATE #T2 SET CLASS=NULL WHERE CLASS='ZZ'
UPDATE #T2 SET NAME=NULL WHERE NAME='ZZ'
UPDATE #T2 SET COURSE=NULL WHERE COURSE='ZZ'

    WITH ROLLUP的結果:

SELECT GRADE,CLASS,NAME,COURSE,SUM(RESULT) RESULT
FROM #T0
GROUP BY GRADE,CLASS,NAME,COURSE
WITH ROLLUP

    自己測試的結果:

SELECT * FROM #T2

    結果與上面一致。

    6、GROUPING

    這個就比較容易理解了,WITH CUBE與WITH ROLLUP用法一樣,先看結果:

SELECT GRADE,CLASS,NAME,COURSE,SUM(RESULT) RESULT,GROUPING(COURSE) [GROUPING]
FROM #T0
GROUP BY GRADE,CLASS,NAME,COURSE
WITH ROLLUP

    上面GROUPING的是COURSE字段,有NULL值就是WITH ROLLUP額外添加的,GROUPING結果值為1。

    有了GROUPING,那做小計、總計就方便了。

SELECT 
    GRADE,
    CASE WHEN GROUPING(GRADE)=1 AND GROUPING(CLASS)=1 THEN '總計' WHEN GROUPING(GRADE)=0 AND GROUPING(CLASS)=1 THEN '小計' ELSE CLASS END CLASS,
    NAME,COURSE,SUM(RESULT) RESULT
FROM #T0
GROUP BY GRADE,CLASS,NAME,COURSE
WITH ROLLUP

     好了,原理測試及應用就到這里結束了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM