GROUP BY·HAVING 완벽 정리
집계함수, ROLLUP, CUBE, GROUPING SETS까지 한눈에 정리
1. 집계 함수 (Aggregate Functions)
주요 집계 함수
| 함수 | 설명 | NULL 처리 |
|---|---|---|
| COUNT(*) | 전체 행 수 | NULL 포함 |
| COUNT(col) | 해당 컬럼의 NULL 아닌 행 수 | NULL 제외 |
| COUNT(DISTINCT col) | 중복 제거 후 행 수 | NULL 제외 |
| SUM(col) | 합계 | NULL 제외 |
| AVG(col) | 평균 (NULL 행 제외 후 계산) | NULL 제외 |
| MAX(col) | 최댓값 | NULL 제외 |
| MIN(col) | 최솟값 | NULL 제외 |
NULL 처리 핵심
집계 함수는 기본적으로 NULL을 무시한다. 단, COUNT(*)만 유일하게 NULL을 포함하여 전체 행 수를 센다. AVG 계산 시 NULL 행은 분모에서도 제외된다.
COUNT 비교 예시
데이터: 부서(A, A, B, NULL, B)
| 함수 | 결과 | 이유 |
|---|---|---|
| COUNT(*) | 5 | NULL 포함 전체 행 |
| COUNT(부서) | 4 | NULL 1건 제외 |
| COUNT(DISTINCT 부서) | 2 | NULL 제외 + 중복 제거 (A, B) |
2. GROUP BY 기본
GROUP BY 규칙
SELECT절에서 집계함수가 아닌 컬럼은 반드시 GROUP BY에 포함해야 한다. 이를 위반하면 SQL 오류가 발생한다.
-- 올바른 예
SELECT 부서, COUNT(*) FROM 사원 GROUP BY 부서;
-- 오류 (이름이 GROUP BY에 없음)
SELECT 부서, 이름, COUNT(*) FROM 사원 GROUP BY 부서;
SQL 실행 순서
GROUP BY를 이해하려면 SQL 실행 순서를 반드시 알아야 한다.
| 순서 | 절 | 설명 |
|---|---|---|
| 1 | FROM | 테이블 접근 |
| 2 | WHERE | 행 필터링 (그룹화 전) |
| 3 | GROUP BY | 그룹화 |
| 4 | HAVING | 그룹 필터링 (그룹화 후) |
| 5 | SELECT | 컬럼 선택, 집계 계산 |
| 6 | ORDER BY | 정렬 |
3. HAVING절
HAVING이란?
GROUP BY로 그룹화된 결과에 조건을 적용하여 필터링하는 절. 집계 함수를 조건으로 사용할 수 있다.
WHERE vs HAVING
| 구분 | WHERE | HAVING |
|---|---|---|
| 실행 시점 | 그룹화 전 (FROM 다음) | 그룹화 후 (GROUP BY 다음) |
| 집계함수 | 사용 불가 | 사용 가능 |
| 필터 대상 | 개별 행 | 그룹(집계 결과) |
| 예시 | WHERE 급여 > 3000 | HAVING COUNT(*) > 5 |
실전 SQL 예시
-- 부서별 인원이 5명 이상인 부서만 조회
SELECT 부서, COUNT(*) AS 인원
FROM 사원
WHERE 퇴사여부 = 'N'
GROUP BY 부서
HAVING COUNT(*) >= 5
ORDER BY 인원 DESC;
WHERE로 퇴사자 제외(행 필터) → GROUP BY로 부서별 그룹화 → HAVING으로 5명 이상만 필터
4. 고급 그룹 함수
ROLLUP
지정된 컬럼의 소계와 총계를 자동 생성한다. 인수 순서가 중요하며, 계층적으로 집계된다.
GROUP BY ROLLUP(부서, 직급)
-- 결과: (부서,직급) + (부서) 소계 + 총계
ROLLUP(A, B)의 결과 행: (A,B) 그룹 + (A) 소계 + () 총계 → N+1개 레벨의 소계 생성 (인수가 N개일 때)
CUBE
지정된 컬럼의 모든 조합에 대한 소계를 생성한다. 인수 순서와 무관하게 동일한 결과를 반환한다.
GROUP BY CUBE(부서, 직급)
-- 결과: (부서,직급) + (부서) + (직급) + 총계
CUBE(A, B)의 결과 행: (A,B) + (A) + (B) + () → 2^N개 레벨의 소계 생성 (인수가 N개일 때)
GROUPING SETS
특정 그룹만 선택적으로 집계할 수 있다. ROLLUP, CUBE와 달리 원하는 조합만 명시한다.
GROUP BY GROUPING SETS(부서, 직급)
-- 결과: (부서) 소계 + (직급) 소계만 생성
-- (부서,직급) 조합이나 총계는 생성되지 않음
GROUPING() 함수
소계/총계 행과 원본 데이터 행을 구분하는 함수.
| 반환값 | 의미 |
|---|---|
| 0 | 해당 컬럼이 그룹화에 사용됨 (원본 데이터) |
| 1 | 해당 컬럼이 집계에 의해 NULL됨 (소계/총계 행) |
SELECT
CASE WHEN GROUPING(부서) = 1 THEN '전체'
ELSE 부서 END AS 부서,
COUNT(*) AS 인원
FROM 사원
GROUP BY ROLLUP(부서);
5. ROLLUP vs CUBE vs GROUPING SETS 비교
핵심 비교 테이블
| 구분 | ROLLUP | CUBE | GROUPING SETS |
|---|---|---|---|
| 소계 범위 | 계층적 소계 + 총계 | 모든 조합의 소계 + 총계 | 명시한 조합만 |
| 인수 순서 | 중요 (결과 달라짐) | 무관 (결과 동일) | 무관 |
| 소계 레벨 수 | N+1 | 2^N | 명시한 수만큼 |
| 총계 | 자동 포함 | 자동 포함 | () 명시해야 포함 |
ROLLUP(A,B) vs CUBE(A,B) 결과 행 비교
| 그룹 조합 | ROLLUP | CUBE |
|---|---|---|
| (A, B) | O | O |
| (A) 소계 | O | O |
| (B) 소계 | X | O |
| () 총계 | O | O |
ROLLUP(A,B)은 (B)만의 소계를 생성하지 않는다. CUBE는 모든 조합을 생성하므로 (B) 소계도 포함.
6. 시험 빈출 포인트
자주 출제되는 유형
- • SELECT절 집계함수 규칙 — 집계함수 아닌 컬럼은 반드시 GROUP BY에 포함
- • HAVING vs WHERE 차이 — WHERE는 그룹화 전, HAVING은 그룹화 후 필터링
- • COUNT(*) vs COUNT(col) — NULL 포함 여부가 핵심 차이
- • ROLLUP 결과 행 수 계산 — 인수 N개일 때 N+1 레벨의 소계
- • ROLLUP 인수 순서 — ROLLUP(A,B)와 ROLLUP(B,A)는 결과가 다름
- • GROUPING() 함수 반환값 — 0은 원본, 1은 소계/총계 행
- • SQL 실행 순서 — FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
개념을 확인했다면 문제로 실력을 검증해보세요