그룹 함수 ROLLUP·CUBE·GROUPING SETS는 GROUP BY 결과에 소계와 총계 줄을 더합니다. GROUPING 함수는 어느 줄이 합계를 낸 줄인지 1과 0으로 알려 줍니다.
학과·학년마다 학생 수를 세다 보면 학과별 소계와 전체 총계도 함께 보고 싶어집니다. 그룹과 집계: GROUP BY·HAVING의 GROUP BY 하나로는 학과·학년마다의 줄만 나옵니다. 그룹 함수를 쓰면 GROUP BY 절 하나로 소계와 총계를 함께 냅니다. 실기 출제기준도 그룹 함수로 소계·중계·총합계를 내는 DML을 적어 둡니다. 자료에 따라 COUNT·SUM 같은 집계 함수까지 묶어 그룹 함수라고 부르기도 합니다. 이 레슨에서는 ROLLUP·CUBE·GROUPING SETS를 가리킵니다.
그림은 학과코드가 NULL인 강도현(107)을 뺀 학생 6행으로 세다가, 마지막에 강도현을 넣습니다.
1ROLLUP — 소계와 총계 줄
GROUP BY 학과코드, 학년은 학과와 학년이 같은 학생끼리 한 줄로 셉니다. CS-2 2명, CS-3 1명, EE-1 1명, EE-3 2명, 4줄입니다.
GROUP BY ROLLUP(학과코드, 학년)으로 바꾸면 학과마다 소계 줄이 끼어들고, 맨 끝에 총계 줄이 붙습니다. CS 3명, EE 3명, 총계 6명으로 7줄입니다. 소계 줄에서 묶지 않은 열(학년)은 NULL로 보입니다.
2CUBE와 GROUPING SETS — 어떤 묶음을 낼까
CUBE(학과코드, 학년)는 학년별 소계까지 더합니다. 1학년 1명, 2학년 2명, 3학년 3명 세 줄이 더해져 10줄입니다.
GROUPING SETS(학과코드, 학년)는 괄호 안에 적은 묶음, 곧 학과별 소계와 학년별 소계만 냅니다. 상세 줄과 총계 줄이 없어 5줄입니다.
ROLLUP은 오른쪽 열부터 하나씩 빼며 묶음을 만들고, CUBE는 가능한 모든 조합을 만듭니다. ()는 아무 열로도 묶지 않은 총계입니다.
| 그룹 함수 | 열 둘(A, B)일 때 묶음 | 열이 n개일 때 묶음 수 |
|---|---|---|
| ROLLUP | (A, B) · (A) · () | n + 1 |
| CUBE | (A, B) · (A) · (B) · () | 2ⁿ |
| GROUPING SETS | 괄호 안에 적은 것만 | 적은 수 |
- 묶음 수는 결과 행 수가 아닙니다. 위 ROLLUP은 묶음이 3가지이고 결과는 7행입니다.
- ROLLUP은 열 순서를 바꾸면 소계가 달라집니다. CUBE는 열 순서를 바꿔도 같은 묶음이 나옵니다.
3GROUPING — 데이터의 NULL과 합계 줄의 NULL
강도현까지 넣으면 학과코드가 NULL인 줄이 데이터의 NULL인지 합계 줄의 NULL인지 겉으로 가릴 수 없습니다.
GROUPING(학과코드)는 합계를 내느라 학과코드를 비운 줄에서 1, 나머지 줄에서 0을 돌려줍니다.
예제
결과 표는 Oracle에서 돌린 결과입니다. Oracle은 오름차순 정렬에서 NULL을 맨 뒤에 두므로, ORDER BY 학과코드, 학년이면 학과마다 소계 줄이 그 학과 줄들 바로 아래에 옵니다.
ROLLUP
SELECT 학과코드,
학년,
COUNT(*) AS 인원
FROM 학생
WHERE 학과코드 IS NOT NULL
GROUP BY ROLLUP(학과코드, 학년)
ORDER BY 학과코드, 학년;
| 학과코드 | 학년 | 인원 |
|---|---|---|
| CS | 2 | 2 |
| CS | 3 | 1 |
| CS | NULL | 3 |
| EE | 1 | 1 |
| EE | 3 | 2 |
| EE | NULL | 3 |
| NULL | NULL | 6 |
(CS, NULL)은 CS 학과의 소계, (NULL, NULL)은 총계입니다.
CUBE
SELECT 학과코드,
학년,
COUNT(*) AS 인원
FROM 학생
WHERE 학과코드 IS NOT NULL
GROUP BY CUBE(학과코드, 학년)
ORDER BY 학과코드, 학년;
| 학과코드 | 학년 | 인원 |
|---|---|---|
| CS | 2 | 2 |
| CS | 3 | 1 |
| CS | NULL | 3 |
| EE | 1 | 1 |
| EE | 3 | 2 |
| EE | NULL | 3 |
| NULL | 1 | 1 |
| NULL | 2 | 2 |
| NULL | 3 | 3 |
| NULL | NULL | 6 |
ROLLUP 결과에 학년별 소계 세 줄이 더해졌습니다. (NULL, 3) 3명은 CS 3학년 1명과 EE 3학년 2명의 합입니다.
GROUPING SETS
SELECT 학과코드,
학년,
COUNT(*) AS 인원
FROM 학생
WHERE 학과코드 IS NOT NULL
GROUP BY GROUPING SETS(
학과코드, 학년)
ORDER BY 학과코드, 학년;
| 학과코드 | 학년 | 인원 |
|---|---|---|
| CS | NULL | 3 |
| EE | NULL | 3 |
| NULL | 1 | 1 |
| NULL | 2 | 2 |
| NULL | 3 | 3 |
학과별로 센 결과와 학년별로 센 결과를 위아래로 이어 붙인 것과 같습니다.
107까지 넣으면 — GROUPING
WHERE를 빼면 학과코드가 NULL인 강도현이 들어옵니다.
ORDER BY 학과코드, 학년만으로는 (NULL, NULL)인 두 줄의 차례가 정해지지 않아, GROUPING으로 먼저 정렬합니다.
SELECT 학과코드,
학년,
COUNT(*) AS 인원,
GROUPING(학과코드) AS G
FROM 학생
GROUP BY ROLLUP(학과코드, 학년)
ORDER BY GROUPING(학과코드),
학과코드, 학년;
| 학과코드 | 학년 | 인원 | G |
|---|---|---|---|
| CS | 2 | 2 | 0 |
| CS | 3 | 1 | 0 |
| CS | NULL | 3 | 0 |
| EE | 1 | 1 | 0 |
| EE | 3 | 2 | 0 |
| EE | NULL | 3 | 0 |
| NULL | 1 | 1 | 0 |
| NULL | NULL | 1 | 0 |
| NULL | NULL | 7 | 1 |
- (NULL, 1) 1명은 학과가 없는 1학년 강도현의 줄입니다. 학과코드 NULL은 데이터의 NULL입니다.
- (NULL, NULL)이 두 줄입니다. G가 0인 1명 줄은 학과가 NULL인 그룹의 소계이고, G가 1인 7명 줄이 총계입니다.
- 이렇게 섞이지 않게 하려면 앞의 예제처럼 WHERE로 NULL인 행을 빼거나, GROUPING으로 가릅니다.
시험 포인트
- 필기: ROLLUP은 n + 1가지, CUBE는 2ⁿ가지 묶음으로 합계를 냅니다. ROLLUP은 열 순서를 바꾸면 결과가 달라지고, CUBE는 같습니다. 소계·총계 줄에서 묶지 않은 열은 NULL로 보이며, 데이터의 NULL과는 GROUPING으로 가립니다.
- 실기: 그룹 함수를 쓴 쿼리의 결과를 소계·총계 줄까지 행 차례대로 씁니다. 설명을 보고 ROLLUP·CUBE·GROUPING SETS·GROUPING을 가려 씁니다.
확인 문제
1. 수강 표를 GROUP BY CUBE(과목코드, 학번)으로 묶습니다. 합계를 내는 묶음으로 알맞은 것을 고르세요.
- ① (과목코드, 학번) · (과목코드)
- ② (과목코드, 학번) · (과목코드) · ()
- ③ (과목코드, 학번) · (과목코드) · (학번)
- ④ (과목코드, 학번) · (과목코드) · (학번) · ()
정답
④입니다. CUBE는 적은 열로 만들 수 있는 모든 조합으로 묶습니다. 열이 둘이면 2²이라 네 가지이고, 아무 열로도 묶지 않은 총계 ()도 그 가운데 하나입니다.
- ① ROLLUP의 묶음에서 총계까지 뺀 꼴입니다. 묶음은 ROLLUP도 n + 1가지라 셋입니다.
- ② ROLLUP(과목코드, 학번)의 묶음입니다. 오른쪽 열부터 하나씩 빼므로 학번만으로 묶은 소계가 없습니다.
- ③ 총계를 빼먹었습니다. 2ⁿ의 n에는 열 수를 넣고, 그 가운데 하나가 총계입니다.
2. 수강 표를 과목코드와 학번으로 묶어 점수 합계를 냅니다. (가)~(라)에 맞는 함수의 이름을 쓰세요. 답을 끝까지 쓴 뒤 정답을 펼치세요.
- (가) 과목코드별 소계와 맨 끝의 총계는 내고, 학번별 소계는 내지 않습니다.
- (나) (가)의 결과에 학번별 소계까지 더해 냅니다.
- (다) 과목코드별 소계와 학번별 소계만 내고, 상세 줄과 총계 줄은 내지 않습니다.
- (라) 결과에 보이는 NULL이 데이터에 있던 값인지, 소계를 내며 비워진 칸인지 숫자로 알려 줍니다.
정답
받는 답: (가) ROLLUP · (나) CUBE · (다) GROUPING SETS · (라) GROUPING
(가)는 왼쪽 열부터 차례로만 소계를 내고, (나)는 모든 조합으로 냅니다. (다)는 괄호 안에 적은 묶음만 내 상세 줄과 총계 줄이 없습니다. (라)는 줄을 만들지 않고 가려 주기만 합니다.
- 틀리는 꼴 (다)에 GROUPING: 이름이 비슷하지만 줄을 만드는 쪽은 GROUPING SETS입니다. SETS까지 적어야 받습니다.
- 틀리는 꼴 (나)에 ROLLUP(학번, 과목코드): 열 순서를 바꿔도 소계가 생기는 열이 하나뿐입니다. 둘 다 내려면 CUBE입니다.
- 틀리는 꼴 (라)에 GROUP BY: 묶으라고 알려 주는 절이지 함수가 아닙니다.
3. 수강 표의 점수 합계를 냅니다. (1)~(3)을 차례로 풀어 보세요. 답을 끝까지 쓴 뒤 정답을 펼치세요.
(1) 아래 쿼리의 결과 표에서 (가)·(나)를 채우세요.
| 과목코드 | 합계 |
|---|---|
| DS | 320 |
| NW | (가) |
| OS | 210 |
| NULL | (나) |
SELECT 과목코드,
SUM(점수) AS 합계
FROM 수강
GROUP BY ROLLUP(과목코드)
ORDER BY 과목코드;
(2) 같은 쿼리의 ROLLUP만 CUBE로 바꾸면 결과는 몇 행일까요?
(3) 70점 이상인 점수만 보고 과목코드별 합계와 전체 총계를 내는 쿼리를 쓰고, 결과를 쓰세요.
정답
받는 답: (1) (가) 370 · (나) 900 · (2) 4행 · (3) 아래 둘째 블록
(1) NW는 80 + 55 + 95 + 90 + 50 = 370이고, NULL 줄은 12행을 모두 더한 총계 900입니다. (2) 열이 하나면 CUBE의 묶음도 (과목코드)와 () 둘이라 ROLLUP과 같은 4행입니다. 2ⁿ의 n이 1이라 2가지입니다.
| 과목코드 | 합계 |
|---|---|
| DS | 320 |
| NW | 370 |
| OS | 210 |
| NULL | 900 |
(3)은 WHERE로 행을 먼저 거르고 남은 행만 더합니다.
SELECT 과목코드,
SUM(점수) AS 합계
FROM 수강
WHERE 점수 >= 70
GROUP BY ROLLUP(과목코드)
ORDER BY 과목코드;| 과목코드 | 합계 |
|---|---|
| DS | 255 |
| NW | 265 |
| OS | 150 |
| NULL | 670 |
- 틀리는 꼴 (2) 8행: 2ⁿ의 n에 데이터의 과목 수 3을 넣었습니다. n은 묶으려고 적은 열 수입니다.
- 틀리는 꼴 (2) 3행: CUBE가 총계 줄을 내지 않는다고 봤습니다. 열이 하나여도 총계 줄은 붙습니다.
- 틀리는 꼴 (3) NULL 줄에 900: 총계 줄도 WHERE를 거친 행만 더합니다. 걸러진 60·65·55·50은 들어가지 않습니다.
더 자세히: SQLD의 그룹 함수