GROUP BY와 HAVING

핵심 인사이트 (3줄 요약)

  1. 본질: GROUP BY는 행들을 특정 열의 값으로 그룹화하고, 각 그룹에 대해 집계 함수 (COUNT, SUM, AVG 등)를 적용하는 SQL 절이다.
  2. 가치: GROUP BY를 통해 다양한 차원의 데이터 분석이 가능해져, 매출 분석, 통계 보고, 대시보드 데이터 생성 등에 필수적인 도구다.
  3. 융합: 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

구분WHEREHAVING
적용 시점그룹화 이전그룹화 이후
집계 함수 사용불가가능
대상개별 행그룹

예시: 고객 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 등으로, 그룹에 대한 통계 값을 산출한다.
WHEREGROUP BY 이전에 개별 행을 필터링한다.
ROLLUP계층적 부분 합계를 산출하는 GROUP BY 확장이다.
CUBE모든 조합의 부분 합계를 산출하는 GROUP BY 확장이다.

👶 어린이를 위한 3줄 비유 설명

  1. GROUP BY는 조별 과제에서 같은 반 친구를小组로 모으는 것과 같아요. "1반 친구들", "2반 친구들" 등으로 묶으면 各반 평균을 낼 수 있죠.
  2. HAVING은 "조별 과제에서 3명 이상인 조만 발표해"라는 것과 같아요. 그룹 중에서条件에 맞는 것만 선택하는 거예요.
  3. 이렇게 하면 전체 데이터도 보고, 그룹별 분석도 할 수 있어서 훨씬 많은 정보를 얻을 수 있어요!