GROUPING SETS 도 GROUP BY 규칙을 그대로 따른다. 어느 묶음에도 없는 열을 SELECT 하면 ORA-00979 다.
SELECT DEPTNO, JOB, SUM(SAL) AS SUM_SAL FROM EMP GROUP BY GROUPING SETS (DEPTNO, ());
SELECT DEPTNO, JOB, SUM(SAL) AS SUM_SAL FROM EMP GROUP BY GROUPING SETS (DEPTNO, ()) *ERROR at line 1:ORA-00979: "JOB": must appear in the GROUP BY clause or be used in an aggregate function
SELECT CASE GROUPING(DEPTNO) WHEN 1 THEN '총계' ELSE TO_CHAR(DEPTNO) END AS DEPT_L, CASE WHEN GROUPING(DEPTNO) = 1 THEN ' ' WHEN GROUPING(JOB) = 1 THEN '소계' ELSE JOB END AS JOB_L, SUM(SAL) AS SUM_SAL FROM EMP GROUP BY ROLLUP(DEPTNO, JOB) ORDER BY GROUPING(DEPTNO), DEPTNO, GROUPING(JOB), JOB;
DEPTNO 는 숫자라 문자 라벨과 한 열에 담으려고 TO_CHAR로 바꿨다. CASE 의 THEN 값들은 자료형이 맞아야 한다.
ORDER BY 에도 GROUPING 을 써서 상세 → 소계 → 총계 순서를 고정했다. NULL 의 정렬 위치에 기대지 않는다.
DECODE 로도 같은 일을 한다. CUBE 결과에 '전체' 라벨을 붙인다.
SELECT DECODE(GROUPING(DEPTNO), 1, '전체', DEPTNO) AS DEPT_L, DECODE(GROUPING(JOB), 1, '전체', JOB) AS JOB_L, SUM(SAL) AS SUM_SAL FROM EMP GROUP BY CUBE(DEPTNO, JOB) ORDER BY GROUPING(DEPTNO), DEPTNO, GROUPING(JOB), JOB;
DEPT_L JOB_L SUM_SAL---------------------------------------- --------- ----------10 CLERK 130010 MANAGER 245010 PRESIDENT 500010 전체 875020 ANALYST 600020 CLERK 190020 MANAGER 297520 전체 1087530 CLERK 95030 MANAGER 285030 SALESMAN 560030 전체 9400전체 ANALYST 6000전체 CLERK 4150전체 MANAGER 8275전체 PRESIDENT 5000전체 SALESMAN 5600전체 전체 29025
DECODE(GROUPING(DEPTNO), 1, '전체', DEPTNO)는 첫 결과값이 문자라 DEPTNO 숫자를 문자로 바꿔 돌려준다.
두 열이 모두 '전체'인 마지막 행이 총계, 한쪽만 '전체'인 행이 소계다.
⚠️ 함정: 라벨 열의 별칭을 원래 열 이름과 같게 두면 ORDER BY 가 그 이름을 별칭으로 읽는다. AS JOB으로 짓고 ORDER BY GROUPING(JOB)을 쓰면 GROUPING 안의 JOB 이 집계식이 든 별칭을 가리켜 ORA-00934 가 난다(이 노트의 실습 환경인 Oracle 23ai 기준). 라벨 별칭은 다른 이름으로 짓는다.
SELECT DECODE(GROUPING(JOB), 1, '합계', JOB) AS JOB, SUM(SAL) AS SUM_SAL FROM EMP GROUP BY ROLLUP(JOB) ORDER BY GROUPING(JOB), JOB;
SELECT DECODE(GROUPING(JOB), 1, '합계', JOB) AS JOB, SUM(SAL) AS SUM_SAL FROM EMP GROUP BY ROLLUP(JOB) ORDER BY GROUPING(JOB), JOB *ERROR at line 1:ORA-00934: group function is not allowed here
GROUPING 은 HAVING 에도 쓸 수 있다. 소계 행만 남기거나 빼는 데 쓴다.
SELECT DEPTNO, JOB, SUM(SAL) FROM EMP GROUP BY GROUPING SETS ((DEPTNO, JOB), DEPTNO, ()) HAVING GROUPING(JOB) = 1 ORDER BY DEPTNO;