GROUP BY와 HAVING
핵심 인사이트 (3줄 요약)
- 본질: GROUP BY는 행들을 특정 열의 값으로 그룹화하고, 각 그룹에 대해 집계 함수 (COUNT, SUM, AVG 등)를 적용하는 SQL 절이다.
- 가치: GROUP BY를 통해 다양한 차원의 데이터 분석이 가능해져, 매출 분석, 통계 보고, 대시보드 데이터 생성 등에 필수적인 도구다.
- 융합: OLAP (Online Analytical Processing), 데이터 웨어하우스, BI 도구와 결합하여 기업 데이터 분석의 핵심 인프라로 활용된다.
Ⅰ. 개요 및 필요성 (Context & Necessity)
개념 정의
GROUP BY는 특정 열의 값이 동일한 행들을 하나의 그룹으로 모으는 SQL 절이다. 각 그룹에 대해 집계 함수를 적용하여 통계 정보를 산출한다.
왜 필요한가?
전체 합계뿐 아니라 카테고리별, 기간별, 지역별 등 다양한 차원의 분석이 필요하다. GROUP BY는 이러한 분석을可能하게 한다.
┌─────────────────────────────────────────────────────────────────────┐
│ GROUP BY 필요성 예시 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ [GROUP BY 없이: 전체 주문 금액 합계] │
│ SELECT SUM(amount) FROM orders; │
│ → 전체 합계만 알 수 있음 │
│ │
│ [GROUP BY 사용: 도시별 주문 금액 합계] │
│ SELECT city, SUM(amount) │
│ FROM orders o │
│ JOIN customers c ON o.customer_id = c.customer_id │
│ GROUP BY city; │
│ │
│ → 서울: 500,000원 / 부산: 300,000원 / 대구: 200,000원 │
│ → 도시별 분석 가능! │
│ │
└─────────────────────────────────────────────────────────────────────┘
비유
GROUP BY는 반별로 조별 만들기와 같다. 전체 학생 중에서 같은 반 학생끼리 그룹을 만들고, 각 그룹의 평균 키, 평균 몸무게 등을計算한다.
- **📢 섹션 요약 비유:**학급 전체의 평균을 내기보다는, 반별, 과목별 평균을 내면 더 의미 있는 정보가 되는 것처럼, GROUP BY는 데이터를 의미 있는 그룹으로 分析할 수 있게 합니다.
Ⅱ. GROUP BY 기본
기본 문법
SELECT 열1, 열2, ..., 집계함수(열)
FROM 테이블
GROUP BY 열1, 열2, ...;
예시: 도시별 고객 수
SELECT city, COUNT(*) as customer_count
FROM customers
GROUP BY city;
┌─────────────────────────────────────────────────────────────────────┐
│ GROUP BY 동작 예시 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ customers 테이블: │
│ ┌────────┬──────────┬────────┐ │
│ │ 고객ID │ 이름 │ 도시 │ │
│ ├────────┼──────────┼────────┤ │
│ │ C001 │ 김철수 │ 서울 │ │
│ │ C002 │ 이영희 │ 부산 │ │
│ │ C003 │ 박민수 │ 서울 │ │
│ │ C004 │ 최지수 │ 부산 │ │
│ └────────┴──────────┴────────┘ │
│ │
│ Query: SELECT city, COUNT(*) FROM customers GROUP BY city; │
│ │
│ Result: │
│ ┌────────┬──────────────┐ │
│ │ city │ COUNT(*) │ │
│ ├────────┼──────────────┤ │
│ │ 서울 │ 2 │ ← C001, C003 │
│ │ 부산 │ 2 │ ← C002, C004 │
│ └────────┴──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Ⅲ. HAVING 절
HAVING은 GROUP BY로 형성된 그룹을 필터링하는 조건이다. WHERE가 개별 행을 필터링하는 반면, HAVING은 그룹 단위로 필터링한다.
WHERE vs HAVING
| 구분 | WHERE | HAVING |
|---|---|---|
| 적용 시점 | 그룹화 이전 | 그룹화 이후 |
| 집계 함수 사용 | 불가 | 가능 |
| 대상 | 개별 행 | 그룹 |
예시: 고객 2명 이상인 도시만
SELECT city, COUNT(*) as customer_count
FROM customers
GROUP BY city
HAVING COUNT(*) >= 2;
┌─────────────────────────────────────────────────────────────────────┐
│ WHERE vs HAVING 예시 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ [적용 순서] │
│ FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY │
│ │
│ 예시: 서울/부산만人群中, 2명 이상인 도시 │
│ │
│ SELECT city, COUNT(*) │
│ FROM customers │
│ WHERE city IN ('서울', '부산') -- ① 그룹화 전 필터링 │
│ GROUP BY city -- ② 그룹화 │
│ HAVING COUNT(*) >= 2 -- ③ 그룹 결과 필터링 │
│ ORDER BY COUNT(*) DESC; -- ④ 정렬 │
│ │
│ Result: │
│ ┌────────┬──────────────┐ │
│ │ 서울 │ 2 │ │
│ │ 부산 │ 2 │ │
│ └────────┴──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Ⅳ. 다중 열 GROUP BY
예시: 도시별, 등급별 고객 수
SELECT city, grade, COUNT(*) as cnt
FROM customers
GROUP BY city, grade
ORDER BY city, grade;
┌─────────────────────────────────────────────────────────────────────┐
│ 다중 열 GROUP BY 예시 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ customers 테이블: │
│ ┌────────┬──────────┬────────┬────────┐ │
│ │ 고객ID │ 이름 │ 도시 │ 등급 │ │
│ ├────────┼──────────┼────────┼────────┤ │
│ │ C001 │ 김철수 │ 서울 │ Gold │ │
│ │ C002 │ 이영희 │ 부산 │ Silver │ │
│ │ C003 │ 박민수 │ 서울 │ Silver │ │
│ │ C004 │ 최지수 │ 부산 │ Gold │ │
│ └────────┴──────────┴────────┴────────┘ │
│ │
│ Query: SELECT city, grade, COUNT(*) FROM customers │
│ GROUP BY city, grade; │
│ │
│ Result: │
│ ┌────────┬────────┬─────┐ │
│ │ city │ grade │ cnt │ │
│ ├────────┼────────┼─────┤ │
│ │ 서울 │ Gold │ 1 │ ← 김철수 │
│ │ 서울 │ Silver │ 1 │ ← 박민수 │
│ │ 부산 │ Gold │ 1 │ ← 최지수 │
│ │ 부산 │ Silver │ 1 │ ← 이영희 │
│ └────────┴────────┴─────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Ⅴ.ROLLUP과 CUBE
ROLLUP: 계층적 부분 합계
SELECT city, grade, COUNT(*) as cnt
FROM customers
GROUP BY ROLLUP (city, grade);
┌─────────────────────────────────────────────────────────────────────┐
│ ROLLUP 결과 예시 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────┬────────┬─────┐ │
│ │ city │ grade │ cnt │ │
│ ├────────┼────────┼─────┤ │
│ │ 서울 │ Gold │ 1 │ ← 세분화 │
│ │ 서울 │ Silver │ 1 │ ← 세분화 │
│ │ 서울 │ NULL │ 2 │ ← 서울 소계 │
│ │ 부산 │ Gold │ 1 │ ← 세분화 │
│ │ 부산 │ Silver │ 1 │ ← 세분화 │
│ │ 부산 │ NULL │ 2 │ ← 부산 소계 │
│ │ NULL │ NULL │ 4 │ ← 전체 합계 │
│ └────────┴────────┴─────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
CUBE: 모든 조합의 부분 합계
SELECT city, grade, COUNT(*) as cnt
FROM customers
GROUP BY CUBE (city, grade);
- **📢 섹션 요약 비유:**ROLLUP은 "전체 합계 + 반별 합계 + 과목별 합계"를 보여주면, CUBE는 가능한 모든 조합의 합계를 보여줘요.
📌 관련 개념 맵 (Knowledge Graph)
| 개념 명칭 | 관계 및 시너지 설명 |
|---|---|
| 집계 함수 | COUNT, SUM, AVG, MAX, MIN 등으로, 그룹에 대한 통계 값을 산출한다. |
| WHERE | GROUP BY 이전에 개별 행을 필터링한다. |
| ROLLUP | 계층적 부분 합계를 산출하는 GROUP BY 확장이다. |
| CUBE | 모든 조합의 부분 합계를 산출하는 GROUP BY 확장이다. |
👶 어린이를 위한 3줄 비유 설명
- GROUP BY는 조별 과제에서 같은 반 친구를小组로 모으는 것과 같아요. "1반 친구들", "2반 친구들" 등으로 묶으면 各반 평균을 낼 수 있죠.
- HAVING은 "조별 과제에서 3명 이상인 조만 발표해"라는 것과 같아요. 그룹 중에서条件에 맞는 것만 선택하는 거예요.
- 이렇게 하면 전체 데이터도 보고, 그룹별 분석도 할 수 있어서 훨씬 많은 정보를 얻을 수 있어요!