데이터 그룹화 – 필터링

GROUP BY 절을 통해 데이터를 그룹화하고 집계한 후, 특정 조건을 만족하는 그룹만을 필터링해야 할 때가 있다. 이때 사용하는 것이 HAVING 절이다. WHERE 절과 HAVING 절은 모두 필터링 기능을 하지만, 작동 시점과 대상이 명확히 다르다

WHERE 절과 HAVING 절의 차이

  • WHERE 절: GROUP BY 이전에 동작하며, 개별 행(Row)에 조건을 적용하여 필터링한다. 집계 함수를 사용할 수 없다
  • HAVING 절: GROUP BY 이후에 동작하며, 그룹(Group)에 조건을 적용하여 필터링한다. 집계 함수를 사용할 수 있다

SQL 쿼리 실행 순서

  • FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY

이 순서를 이해하는 것이 WHERE와 HAVING을 올바르게 사용하는 핵심이다. WHERE는 데이터를 그룹화하기 전에 먼저 개별 데이터를 걸러내고 HAVING은 그렇게 그룹화된 결과물 중에서 특정 조건을 만족하는 그룹만 최종적으로 선택한다

잘못된 예시 ( WHERE 에서 집계 함수 사용 시 오류)

SELECT
    category,
    SUM(price * quantity) AS total_sales
FROM
    order_stat
WHERE
    SUM(price * quantity) >= 500000 -- 오류 발생: WHERE 절에서는 집계 함수 사용 불가
GROUP BY
    category;
  • WHERE 절은 개별 행을 검사하는 시점에는 SUM()과 같은 집계 함수의 결과값이 아직 계산되지 않으므로, 위 쿼리는 Invalid use of group function 오류를 발생시킨다

HAVING 절의 사용법

  • HAVING 절은 GROUP BY 절 바로 뒤에 위치하며, 그룹화된 결과에 대한 조건을 지정한다

매출액 50만원 이상인 핵심 카테고리 필터링

SELECT
    category,
    SUM(price * quantity) AS total_sales
FROM
    order_stat
GROUP BY
    category
HAVING
    SUM(price * quantity) >= 500000; -- 그룹화된 total_sales에 조건 적용
  • GROUP BY로 카테고리별 매출액을 계산한 후, HAVING 절이 total_sales가 50만원 이상인 그룹(‘가구’, ‘전자기기’)만 최종 결과로 필터링 한다

HAVING에서 별칭 사용 시 유의사항

  • MySQL, PostgreSQL 등 일부 DBMS는 HAVING 절에서 SELECT 절의 별칭 (예: total_sales)사용을 허용하지만, SQL 표준에는 위배된다. SQL Server, Oracle 등 표준을 엄격히 따르는 DBMS에서는 오류가 발생할 수 있다. 따라서 여러 데이터베이스 환경에서 호환성을 높이려면 HAVING 절에 SUM(price * quantity)와 같이 집계 함수 표현식을 직접 사용하는 것이 안전하다

3회 이상 주문한 충성 고객 필터링

SELECT
    customer_name,
    COUNT(*) AS order_count
FROM
    order_stat
GROUP BY
    customer_name
HAVING
    COUNT(*) >= 3; -- 그룹화된 order_count에 조건 적용
  • 고객별 주문 횟수를 집계한 후, HAVING 절이 주문 횟수가 3회 이상인 고객 (‘세종대왕’, ‘이순신’) 그룹만 필터링하여 보여준다

WHERE와 HAVING 함께 사용하기

요구사항
  • 가격이 10만원 이상인 고가 상품들 중에서만, 카테고리별로 묶었을 때, 해당 고가 상품이 2건 이상 팔린 카테고리는 어디인가요?
요구사항 분석
  • WHERE: 개별 주문의 가격이 10만원 이상인 상품만 선택 (price >= 100000)
  • GROUP BY: 위에서 필터링된 상품들을 카테고리별로 그룹화 (category)
  • HAVING: 그룹화된 결과 중 고가 상품 주문 건수가 2건 이상인 그룹만 선택 (COUNT(*) >= 2)
SELECT
    category,
    COUNT(*) AS premium_order_count
FROM
    order_stat
WHERE
    price >= 100000 -- 1단계: 개별 행 필터링 (가격 기준)
GROUP BY
    category        -- 2단계: 필터링된 행들을 카테고리별로 그룹화
HAVING
    COUNT(*) >= 2;  -- 3단계: 그룹화된 결과 필터링 (그룹별 주문 건수 기준)

쿼리 실행 과정

WHERE price >= 100000
  • 전체 11개 주문 중 가격이 10만원 미만인 주문이 먼저 필터링된다.
GROUP BY category
  • 남은 6개 행이 ‘전자기기’, ‘가구’, ‘문구’ 세 그룹으로 묶인다
HAVING COUNT(*) >= 2
  • 각 그룹의 COUNT(*)를 계산하여 2 이상인 그룹만 남긴다. ‘문구’ 그룹은 1건이므로 제외된다

WHERE와 HAVING을 함께 사용하면 데이터를 개별 행 수준에서 한 번, 그리고 그룹 수준에서 다시 한번 필터링하여 매우 정교한 분석 결과를 얻을 수 있다

출처 – 김영한 님의 강의 김영한의 실전 데이터베이스 입문 – 모든 IT인을 위한 SQL 첫걸음(SQL부터 차근차근)