왜 WHERE와 HAVING이 헷갈릴까?
SQL을 공부하다 보면 많은 분들이 이렇게 묻습니다. "도대체 WHERE, HAVING, 그리고 GROUP BY는 어떻게 다른 걸까요?" 특히 집계 함수(Aggregate Function)와 결합될 때 이들 절의 차이가 더욱 중요해집니다. 이 글에서는 각 절의 쓰임과 차이를 단계별로 이해하기 쉽게 풀어보겠습니다.
WHERE란 무엇인가요?
"WHERE"는 그룹핑 전에 각 레코드를 걸러내는 조건절입니다. 마치 요리 전에 재료 중에서 상한 재료를 미리 골라내는 것과 같다고 보면 됩니다.
예제
SELECT *
FROM member
WHERE city = 'Seoul';
이 쿼리는 Seoul에 사는 회원만 선택합니다.
GROUP BY란 무엇인가요?
"GROUP BY"는 같은 값을 가진 데이터를 하나의 그룹으로 묶어주는 절입니다. 마치 색깔별로 구슬을 분류하는 것과 비슷합니다.
예제
SELECT city, COUNT(*) AS cnt
FROM member
GROUP BY city;
이 쿼리는 도시별로 회원 수를 집계합니다.
GROUP BY를 왜 사용해야 할까?
데이터베이스에 저장된 수많은 개별 레코드를 "같은 값으로 그룹화"해야 평균, 합계, 건수 등 집계 연산을 할 수 있기 때문입니다. 단순히 SELECT만으로는 개별 행만 조회할 수 있지만, GROUP BY가 있어야 집계 함수가 의미를 가집니다.
GROUP BY를 사용하는 경우
- 도시별, 지역별, 부서별 등 카테고리별 집계가 필요할 때
- 시간 단위(일, 월, 연도 등)로 데이터를 집계할 때
- 비즈니스 리포트나 통계 데이터를 생성할 때
- 집계한 결과를 HAVING으로 추가 필터링하고 싶을 때
GROUP BY를 잘 활용하면 방대한 데이터를 이해하기 쉽게 요약할 수 있습니다.
HAVING이란 무엇인가요?
"HAVING"은 그룹핑된 결과에 조건을 거는 절입니다. 많은 사람들이 WHERE와 헷갈리는데, 이 둘의 큰 차이는 작동 시점에 있습니다.
- "WHERE": 그룹핑 전에 조건 적용
- "HAVING": 그룹핑 후 집계 결과에 조건 적용
예제
SELECT city, COUNT(*) AS cnt
FROM member
GROUP BY city
HAVING cnt >= 2;
이 쿼리는 인원수가 2명 이상인 도시만 보여줍니다.
HAVING을 왜 사용해야 할까?
WHERE는 개별 레코드에만 조건을 걸 수 있어서, SUM, COUNT 같은 집계 함수가 반환한 값을 바로 필터링할 수 없습니다. 이럴 때 HAVING이 필요합니다.
HAVING을 사용하는 경우
- 집계 결과(예: SUM, COUNT, AVG 등)가 일정 조건 이상이거나 이하인지 검사할 때
- 그룹핑 후 특정 그룹만 추출하고 싶을 때
- 복잡한 분석 로직을 적용해 그룹별로 필터링할 때
HAVING 없이 집계 결과를 필터링할 수 없기 때문에, GROUP BY를 쓴 후 결과를 한 번 더 선별하고 싶다면 반드시 HAVING을 사용해야 합니다.
WHERE와 HAVING 비교하기
같은 조건이라도 WHERE와 HAVING을 어디에 쓰느냐에 따라 결과가 달라집니다.
WHERE 사용
SELECT city, COUNT(*) AS cnt
FROM member
WHERE city = 'Seoul'
GROUP BY city;
이 쿼리는 그룹핑 전에 Seoul만 선택해서 그룹핑합니다.
HAVING 사용
SELECT city, COUNT(*) AS cnt
FROM member
GROUP BY city
HAVING cnt >= 2;
이 쿼리는 먼저 모든 도시를 그룹핑한 뒤, 인원수가 2명 이상인 도시만 남깁니다.
EXPLAIN으로 실행 계획 확인하기
EXPLAIN
명령어를 사용해보면 WHERE 절이 먼저 적용되어 처리할 데이터 양이 줄어드는 것을 확인할 수 있습니다. 이는 성능 최적화에 큰 영향을 미칩니다.
GROUP BY, WHERE, HAVING 함께 사용하는 법
실무에서는 세 절을 함께 사용하는 경우가 많습니다.
- "WHERE"로 우선 불필요한 데이터를 줄이고
- "GROUP BY"로 그룹핑한 후
- "HAVING"으로 그룹 결과에 조건을 걸어 최종 데이터를 필터링합니다.
예제
SELECT city, COUNT(*) AS cnt
FROM member
WHERE city IN ('Seoul', 'Busan')
GROUP BY city
HAVING cnt >= 2;
이 쿼리는 Seoul과 Busan을 대상으로, 인원이 2명 이상인 도시만 보여줍니다.
구문 비교표
구문 | 동작 시점 | 주 사용 목적 |
---|---|---|
WHERE | 그룹핑 전 | 개별 레코드 필터링 |
GROUP BY | 그룹핑 | 집계 데이터 생성 |
HAVING | 그룹핑 후 | 그룹 결과 필터링 |
예제
GROUP BY만 사용하는 예제
SELECT city, COUNT(*) AS cnt
FROM member
GROUP BY city;
GROUP BY와 HAVING을 사용하는 예제
SELECT city, COUNT(*) AS cnt
FROM member
GROUP BY city
HAVING cnt >= 2;
WHERE, GROUP BY, HAVING을 함께 사용하는 예제
SELECT city, COUNT(*) AS cnt
FROM member
WHERE city IN ('Seoul', 'Busan')
GROUP BY city
HAVING cnt >= 2;
주석 예시:
-- city별로 그룹핑 후, 인원수가 2명 이상인 도시만 출력
중급자를 위한 예제
다음은 매출 데이터를 다루는 좀 더 복잡한 예제입니다.
SELECT region,
SUM(CASE WHEN product_category = 'Electronics' THEN amount ELSE 0 END) AS electronics_sales,
COUNT(*) AS total_orders
FROM orders
WHERE order_date >= '2024-01-01'
GROUP BY region
HAVING SUM(amount) > 100000;
이 쿼리는 2024년 이후 주문 데이터를 대상으로 지역별 매출 합계를 계산하고, 그중 매출이 10만 원 이상인 지역만 출력합니다. HAVING 절은 그룹핑 후 조건을 걸기 때문에 전체 그룹 결과를 필터링할 수 있습니다.
중급자를 위한 예제2
다음은 복잡한 비즈니스 로직을 다루는 상급자용 예제입니다.
SELECT customer_id,
SUM(order_amount) AS total_sales,
AVG(order_amount) AS avg_order,
COUNT(*) AS order_count
FROM orders
WHERE order_status IN ('COMPLETED', 'SHIPPED')
AND YEAR(order_date) = 2024
GROUP BY customer_id
HAVING COUNT(*) > 5 AND AVG(order_amount) > 500;
이 쿼리는 2024년에 완료되거나 배송된 주문 중에서, 5회 이상 주문했고 평균 주문 금액이 500 이상인 고객만 찾아냅니다. 상급자용으로 WHERE, GROUP BY, HAVING을 복합 조건으로 사용하는 예제입니다.
참고 링크
- https://dev.mysql.com/doc/refman/8.0/en/group-by-handling.html
- https://www.w3schools.com/sql/sql_groupby.asp
GROUP BY, WHERE, HAVING의 차이를 제대로 이해하면 데이터 분석과 리포트 작성에서 훨씬 더 효과적으로 SQL을 사용할 수 있습니다. 중요한 것은 "언제 조건을 적용하느냐"를 명확히 아는 것입니다.
'Programing > SQL' 카테고리의 다른 글
MySQL 날짜/시간 함수 정리 및 자주 쓰는 패턴 모음 (MySQL Date and Time Functions) (0) | 2025.07.05 |
---|---|
WHERE 절과 JOIN에서의 쿼리 순서, 정말 중요할까? (0) | 2025.06.30 |
Mockaroo로 대용량 테스트 데이터 쉽게 생성하기 (4) | 2025.06.30 |
SQL :: ORACLE :: 오라클 지우기 (0) | 2008.12.01 |
SQL :: ORACLE :: 오라클 :: 자동증가값 :: 시퀸스 :: SEQUENCE (1) | 2008.11.30 |
SQL :: ORACLE :: 오라클 :: 사용자 계정 생성하기 (0) | 2008.11.30 |
SQL :: 보안 관리 :: 끄적임 (0) | 2008.11.26 |
SQL :: ORACLE :: 휴지인스턴스 (0) | 2008.11.24 |