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,即總計。