1. 程式人生 > >Oracle函式之聚合函式---1、關於grouping與grouping_id

Oracle函式之聚合函式---1、關於grouping與grouping_id

1、作用

grouping與grouping_id都是和group by rollup或group by cube同時出現的,實現了小計與總計的功能。

2、引入此函式目的

在小計與總計的欄位,往往是NULL值,因此不容易區分並且寫SQL時不美觀且麻煩。

3、注意

grouping與grouping_id的欄位,必須是group by的欄位;

與rollup或cube關鍵字一同出現;

當欄位為NULL時,grouping返回1,否則返回0;

當欄位的後續列為NULL時,grouping_id返回1,當欄位的前導列為NULL時,grouping_id返回2(僅當cube才出現此種情況),如果全部為NULL,則返回3,如果全不為NULL,則返回0.

3、示例

3.1、grouping

grouping函式的官方解釋連結如下(11gR2):

程式碼1:

SELECT p_code,
       MONTH,
       COUNT(DISTINCT t_code),
       COUNT(DISTINCT diag_code),
       GROUPING(p_code),
       GROUPING(MONTH)
FROM T1
WHERE p_code <= 100013 
GROUP BY rollup(p_code,MONTH);

結果1如下:



結果1可以看出,哪個值為NULL,對應的grouping欄位為1.但是顯得很不美觀且程式碼有些冗餘。

這段程式碼可以改寫的更為簡潔和美觀:

程式碼2:

SELECT DECODE(GROUPING(p_code),1,'All p_code',p_code) AS p_code,
       DECODE(GROUPING(MONTH),1,'All month',MONTH) AS MONTH,
       COUNT(DISTINCT t_code),
       COUNT(DISTINCT diag_code)
FROM T1
WHERE p_code <= 100013 
GROUP BY rollup(p_code,MONTH);

結果2如下:



從結果2可以看出,想要的結果1步得出。這時典型的小計和總計,當然結果也可以排序。

3.2、grouping_id

grouping_id函式的官方解釋連結如下(11gR2):

程式碼3:

SELECT p_code,
       MONTH,
       GROUPING_id(p_code,MONTH),
       COUNT(DISTINCT t_code),
       COUNT(DISTINCT diag_code)
FROM T1
WHERE p_code <= 100013 
GROUP BY rollup(p_code,MONTH);

程式碼3結果如下:


結果3可以看出,grouping_id也可以實現grouping的功能,但是優勢是可以一次grouping_id多個欄位。

如果只想得到每個值和總計,不想得到小計,則可以改寫。

程式碼4:

SELECT CASE WHEN p_code IS NULL THEN 'All code' ELSE to_char(p_code) END AS p_code,
       CASE WHEN MONTH IS NULL THEN 'All month' ELSE to_char(MONTH) END AS MONTH,
       COUNT(DISTINCT t_code),
       COUNT(DISTINCT diag_code)
FROM T1
WHERE p_code <= 100013 
GROUP BY rollup(p_code,MONTH)
HAVING grouping_id(p_code,MONTH) IN (0,3);

結果4如下:


4、實際需求

需求1:統計2013年每個月以及全年的t_code的和。

程式碼:

--程式碼1
SELECT DECODE(grouping(MONTH),1,'2013 all year',MONTH) AS MONTH,
       COUNT(t_code) AS cnt_t_code
FROM T1
WHERE MONTH BETWEEN 201301 AND 201312
GROUP BY ROLLUP(MONTH);

--程式碼2
SELECT CASE WHEN MONTH IS NOT NULL THEN to_char(MONTH)
            WHEN MONTH IS NULL THEN '2013 all year'
       END AS MONTH,
       COUNT(t_code) AS cnt_t_code
FROM T1
WHERE MONTH BETWEEN 201301 AND 201312
GROUP BY rollup(MONTH);

--程式碼3
SELECT to_char(MONTH),
       COUNT(t_code) AS cnt_t_code
FROM T1
WHERE MONTH BETWEEN 201301 AND 201312
GROUP BY MONTH
UNION ALL
SELECT '2013 all year',
       COUNT(t_code) AS cnt_t_code
FROM T1
WHERE MONTH BETWEEN 201301 AND 201312;

結果:


很顯然,程式碼1最簡潔且高效。

需求2:統計2013年每個月、每個p_code(<=100013)所包含的t_code的和以及全年每個p_code(<=100013)的和以及全年所有p_code(<=100013)的和。

程式碼:

SELECT DECODE(GROUPING(MONTH),1,'all year',MONTH) AS MONTH,
       DECODE(GROUPING(p_code),1,'all p_code',p_code) AS p_code,
       COUNT(t_code) AS cnt_t_code
FROM T1
WHERE MONTH BETWEEN 201301 AND 201312
AND p_code <= 100013
GROUP BY CUBE(MONTH,p_code)
HAVING grouping_id(MONTH,p_code)IN (0,2,3)
ORDER BY 1,2;

結果:


這個sql用到了grouping與grouping_id共同使用,grouping的作用為了將NULL值(小計與總計)轉化為非NULL,grouping_id的作用是過濾掉每個月的小計數量,只保留0,2,3的值;

0:代表2個欄位都不為NULL;

1:代表每個月的小計(前導列不為NULL,後續列為NULL;此需求不要求這種資料);

2:代表每種p_code的小計(前導列為NULL,後續列不為NULL);

3:2個欄位全部為NULL,即總計。