집계 쿼리를 작성하다 보면 소계, 합계를 한 번에 뽑아야 할 때가 있습니다.
ROLLUP, CUBE, GROUPING SETS는 GROUP BY의 확장 문법으로, 여러 번 쿼리를 실행할 필요 없이 계층적 집계를 한 번에 처리합니다.
Oracle 9i 이상에서 모두 사용 가능합니다.
비교 한눈에 보기
| 구문 | 생성되는 집계 | 특징 |
|---|---|---|
GROUP BY ROLLUP(A, B) |
(A,B), (A), () | 계층적 소계 — 오른쪽에서 왼쪽으로 |
GROUP BY CUBE(A, B) |
(A,B), (A), (B), () | 모든 조합의 소계 |
GROUP BY GROUPING SETS((A,B), (A), ()) |
지정한 조합만 | 원하는 집계만 선택 |
예제 데이터 (EMP, DEPT 테이블)
이하 예제는 Oracle 기본 샘플 테이블(EMP, DEPT)을 기준으로 합니다.
-- 기본 JOIN 구조
SELECT b.dname, a.job, a.sal, a.empno
FROM emp a, dept b
WHERE a.deptno = b.deptno;ROLLUP
GROUP BY에 ROLLUP을 추가하면 지정한 컬럼 순서대로 소계와 전체 합계를 함께 출력합니다.
-- 직군별 급여 합계 + 전체 합계
SELECT job, SUM(sal)
FROM emp
GROUP BY ROLLUP(job);결과 예시:
| JOB | SUM(SAL) |
|---|---|
| ANALYST | 6000 |
| CLERK | 4150 |
| MANAGER | 8275 |
| PRESIDENT | 5000 |
| SALESMAN | 5600 |
| (전체 합계) | 29025 |
-- 부서별, 직군별 소계 + 부서 소계 + 전체 합계
SELECT b.dname, a.job, SUM(a.sal) sal, COUNT(a.empno) emp_count
FROM emp a, dept b
WHERE a.deptno = b.deptno
GROUP BY ROLLUP(b.dname, a.job);집계 레벨:
(dname, job)→ 부서 + 직군별 합계(dname)→ 부서별 소계()→ 전체 합계 (Grand Total)
CUBE
ROLLUP이 계층적 집계라면, CUBE는 지정한 컬럼의 모든 조합에 대해 소계를 생성합니다.
SELECT b.dname, a.job, SUM(a.sal) sal, COUNT(a.empno) emp_count
FROM emp a, dept b
WHERE a.deptno = b.deptno
GROUP BY CUBE(b.dname, a.job);집계 레벨:
(dname, job)→ 부서 + 직군별 합계(dname)→ 부서별 소계(job)→ 직군별 소계 (ROLLUP에는 없음)()→ 전체 합계
컬럼 수가 N개이면 CUBE는 최대 2ⁿ 조합을 생성합니다. 컬럼이 많을수록 결과 행이 기하급수적으로 늘어납니다.
GROUPING SETS
ROLLUP, CUBE 대신 원하는 집계 조합만 직접 지정할 수 있습니다. 불필요한 소계를 제거해 성능과 가독성을 높일 때 유용합니다.
-- 부서+직군 합계와 전체 합계만 (부서별 소계 제외)
SELECT b.dname, a.job, SUM(a.sal) sal
FROM emp a, dept b
WHERE a.deptno = b.deptno
GROUP BY GROUPING SETS((b.dname, a.job), ());
-- 부서별 합계와 직군별 합계만 (교차 없이)
SELECT b.dname, a.job, SUM(a.sal) sal
FROM emp a, dept b
WHERE a.deptno = b.deptno
GROUP BY GROUPING SETS((b.dname), (a.job));ROLLUP을 GROUPING SETS로 표현하면:
-- 아래 두 쿼리는 동일한 결과
GROUP BY ROLLUP(dname, job)
GROUP BY GROUPING SETS((dname, job), (dname), ())GROUPING 함수
ROLLUP/CUBE 결과에서 소계 행인지 일반 집계 행인지 구분할 때 사용합니다.
반환
0→ GROUP BY로 만들어진 일반 집계 행반환
1→ ROLLUP/CUBE가 생성한 소계/합계 행SELECT b.dname, a.job,
SUM(a.sal) sal, COUNT(a.empno) emp_count, GROUPING(b.dname) AS grp_dname, GROUPING(a.job) AS grp_jobFROM emp a, dept b
WHERE a.deptno = b.deptno
GROUP BY CUBE(b.dname, a.job);
활용 — NULL 대신 레이블 표시:
SELECT
CASE WHEN GROUPING(b.dname) = 1 THEN '전체 부서' ELSE b.dname END AS dname,
CASE WHEN GROUPING(a.job) = 1 THEN '전체 직군' ELSE a.job END AS job,
SUM(a.sal) sal
FROM emp a, dept b
WHERE a.deptno = b.deptno
GROUP BY CUBE(b.dname, a.job);소계/합계 행의 NULL을 의미 있는 레이블로 치환할 수 있어 리포트에서 자주 활용됩니다.
GROUPING_ID 함수
여러 컬럼의 GROUPING 값을 비트 조합한 정수로 반환합니다. 집계 레벨을 하나의 숫자로 판별할 수 있어 GROUPING()을 여러 번 쓰는 것보다 편리합니다.
SELECT b.dname, a.job,
SUM(a.sal) sal,
GROUPING_ID(b.dname, a.job) AS grp_id
FROM emp a, dept b
WHERE a.deptno = b.deptno
GROUP BY CUBE(b.dname, a.job);GROUPING_ID 해석 (컬럼 2개 기준):
| GROUPING_ID | dname | job | 의미 |
|---|---|---|---|
| 0 | 0 | 0 | 부서+직군 상세 |
| 1 | 0 | 1 | 부서별 소계 |
| 2 | 1 | 0 | 직군별 소계 |
| 3 | 1 | 1 | 전체 합계 |
-- 전체 합계 행만 필터링
SELECT b.dname, a.job, SUM(a.sal) sal
FROM emp a, dept b
WHERE a.deptno = b.deptno
GROUP BY CUBE(b.dname, a.job)
HAVING GROUPING_ID(b.dname, a.job) = 3;실무 활용 팁
1. 리포트에서 소계 행 강조
SELECT
NVL2(GROUPING(dname) + GROUPING(job),
CASE GROUPING_ID(dname, job)
WHEN 3 THEN '[ 전체 합계 ]'
WHEN 1 THEN '[ ' || dname || ' 소계 ]'
ELSE dname
END, dname) AS dname,
SUM(sal) sal
FROM emp a JOIN dept b ON a.deptno = b.deptno
GROUP BY ROLLUP(b.dname, a.job);2. 불필요한 조합 제거 — GROUPING SETS 우선
CUBE(A, B, C)는 8가지 조합을 만들지만 실제로 필요한 게 3가지뿐이라면 GROUPING SETS로 명시하는 게 성능상 유리합니다.
3. ORDER BY 주의
ROLLUP/CUBE 결과는 기본 정렬이 보장되지 않습니다. 소계 행이 중간에 섞여 나올 수 있으니 ORDER BY에 GROUPING() 값을 포함하세요.
ORDER BY GROUPING(dname), dname, GROUPING(job), job마치며
| 상황 | 추천 |
|---|---|
| 계층적 소계가 필요할 때 | ROLLUP |
| 모든 차원의 교차 집계가 필요할 때 | CUBE |
| 원하는 집계 조합만 뽑을 때 | GROUPING SETS |
| 소계 행 식별 / 레이블 치환 | GROUPING, GROUPING_ID |
'Dev > DB & Infra' 카테고리의 다른 글
| [오라클 SQL 강좌] 뷰 (View) (0) | 2012.08.28 |
|---|---|
| [ORACLE 강좌] 오라클 함수 - 분석함수 ( Analytic Functions ) (0) | 2012.08.22 |
| [ORACLE 강좌] 오라클 함수 - 집합함수 ( Aggregate functions ) (0) | 2012.08.22 |
| [SQL 문제] 함수(FUNCTION) 통합 테스트 (0) | 2012.08.22 |
| [SQL 문제] 집합함수, group by 이용 간단 테스트 예제 (0) | 2012.08.22 |