Programing/SQL

GROUP BY, WHERE와 HAVING의 차이(SQL Basics Explained)

2025. 7. 3. 07:40
반응형

그룹화된 데이터 필터링에 대한 시각적 분석

 


왜 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
'Programing/SQL' 카테고리의 다른 글
  • MySQL 날짜/시간 함수 정리 및 자주 쓰는 패턴 모음 (MySQL Date and Time Functions)
  • WHERE 절과 JOIN에서의 쿼리 순서, 정말 중요할까?
  • Mockaroo로 대용량 테스트 데이터 쉽게 생성하기
  • SQL :: ORACLE :: 오라클 지우기
Dongkkase
Dongkkase
개발자로 일하면서 부딪히는 문제풀이가 누군가에게 도움이 되길 바라며
    반응형
  • Dongkkase
    정집사의 개발로그
    Dongkkase
  • 전체
    오늘
    어제
    • All (478)
      • 금융 (61)
      • Programing (295)
        • Algorithm (39)
        • API (2)
        • javascript (122)
        • CSS (8)
        • HTML (10)
        • PHP (15)
        • JAVA (27)
        • JSP (17)
        • JSP 예제 (1)
        • IOS (1)
        • Android (1)
        • Sencha Touche (1)
        • bat file, cmd (0)
        • 디버깅 (2)
        • SQL (21)
        • MS-SQL (1)
        • MySQL (13)
        • 보안 (5)
      • Server (14)
        • Docker (1)
        • Windows (9)
        • Linux (3)
        • jeus (1)
      • Database (6)
      • IT 일반 (15)
      • 리뷰 (38)
        • Book (17)
        • 제품 (2)
        • 영화 소개 (11)
        • 음악 소개 (7)
      • 잡생각 (36)
        • 회고 (3)
        • 컬럼 (4)
        • 자료실 (6)
        • 낙서장 (12)
        • 위시리스트 (2)
        • WOW (1)
        • 덕 (1)
  • 인기 글

  • 최근 댓글

  • 태그

    사고 싶은 책
    SQL
    읽고 싶은 책
    IT·컴퓨터
    디자인패턴
    jsp
    위시리스트
    IT블로그
    블로그
    IT 관련
    Java
    It
    자바
    자바스크립트
    iT's MY LiFE
    js패턴
    JavaScript
    기초
    자바스크립트유틸
    php
Dongkkase
GROUP BY, WHERE와 HAVING의 차이(SQL Basics Explained)
상단으로

티스토리툴바