본문 바로가기

비개발자의 개발 일지

현장 팀장에서 수출 ERP까지 — AI와 함께 시스템을 만든 기록

시리즈 보기
php

MySQL HAVING — GROUP BY 결과 필터링 완전 정리

by 왕진 2026. 7. 14.
반응형

 

 

Web Development · MySQL

MySQL HAVING —
GROUP BY 결과 필터링 완전 정리

HAVING vs WHERE · 집계함수 조건 · WHERE + GROUP BY + HAVING 전체 흐름
시작 오늘 분석할 코드
-- 총 구매액이 1000 초과인 회원만 SELECT userID AS '사용자', SUM(price*amount) AS '총구매액' FROM buytbl GROUP BY userID HAVING SUM(price*amount) > 1000 ; -- 집계함수(SUM)를 필터링하기 때문에 WHERE가 아닌 HAVING 사용
HAVING은 WHERE와 달리 GROUP BY 이후에 적용된다. 집계 결과(SUM, COUNT, AVG...)에 조건을 걸 때만 쓴다. 개별 행 조건은 여전히 WHERE로 걸면 된다.

1 HAVING이 필요한 이유

WHERE는 개별 행을 필터링하지만, GROUP BY로 집계한 결과에 조건을 걸 수는 없다. 집계 결과(SUM > 1000)는 GROUP BY 이후에 생성되기 때문이다.

-- ❌ 잘못된 방법: WHERE에 집계함수 사용 시 오류 SELECT userID, SUM(price*amount) FROM buytbl WHERE SUM(price*amount) > 1000 -- Error: Invalid use of group function GROUP BY userID; -- ✅ 올바른 방법: HAVING 사용 SELECT userID, SUM(price*amount) FROM buytbl GROUP BY userID HAVING SUM(price*amount) > 1000;

2 WHERE vs HAVING 차이
구분 WHERE HAVING
적용 대상 개별 행 (GROUP BY 이전) 그룹 결과 (GROUP BY 이후)
실행 시점 GROUP BY 이전 GROUP BY 이후
집계함수 사용 불가 (오류) 가능 (필수 시 사용)
일반 컬럼 비교 가능 가능 (하지만 WHERE로 먼저 거르는 게 성능상 유리)
예시 WHERE price > 100 HAVING SUM(price*amount) > 1000
HAVING도 일반 컬럼 조건을 받을 수 있지만, 그 경우 WHERE가 훨씬 빠르다. HAVING은 집계 결과 전체에 적용되지만 WHERE는 집계 전에 불필요한 행을 미리 제거한다.

3 WHERE + GROUP BY + HAVING 조합

WHERE로 먼저 행을 줄이고, GROUP BY로 그룹화하고, HAVING으로 그룹 결과를 필터링한다. 세 가지를 조합하는 것이 실무에서 가장 자주 쓰는 패턴이다.

-- ① WHERE로 먼저 모니터 제외 -- ② GROUP BY로 회원별 그룹화 -- ③ HAVING으로 총 구매액 500 이상만 SELECT userID AS '회원', SUM(price*amount) AS '총구매액' FROM buytbl WHERE prodName != '모니터' GROUP BY userID HAVING SUM(price*amount) >= 500 ORDER BY 총구매액 DESC;
실행 단계 키워드 역할
FROM buytbl 테이블 선택
WHERE prodName != '모니터' 개별 행 필터 (집계 전)
GROUP BY userID userID별 그룹화
SUM(price*amount) 그룹별 집계
HAVING SUM() >= 500 그룹 결과 필터
SELECT userID, SUM() 출력 컬럼 선택 + AS 별칭
ORDER BY 총구매액 DESC 정렬

 

반응형

 

4 HAVING 다양한 활용
-- 구매 건수 3건 이상 회원 SELECT userID, COUNT(*) AS 구매건수 FROM buytbl GROUP BY userID HAVING COUNT(*) >= 3; -- 평균 단가 200 이상인 그룹 SELECT groupName, AVG(price) AS 평균단가 FROM buytbl GROUP BY groupName HAVING AVG(price) >= 200; -- 최고 단가가 500 이상인 그룹 SELECT groupName, MAX(price) AS 최고단가 FROM buytbl GROUP BY groupName HAVING MAX(price) >= 500; -- AS 별칭을 HAVING에서 사용 (MySQL 허용) SELECT userID, SUM(price*amount) AS 총구매액 FROM buytbl GROUP BY userID HAVING 총구매액 > 1000; -- MySQL에서는 허용 (표준 SQL과 다름)
MySQL은 HAVING 절에서 SELECT에 정의한 AS 별칭을 참조할 수 있다. 하지만 표준 SQL에서는 HAVING은 SELECT 이전에 실행되므로 별칭 참조가 불가능하다. 이식성이 중요하면 원래 식을 그대로 써야 한다.

5 HAVING + ORDER BY 조합

HAVING으로 필터링된 그룹을 정렬까지 하면 실무 보고서 형태가 완성된다. 예: "총 구매액 1000 초과 회원, 많이 산 순으로"

-- 총 구매액 1000 초과, 많이 산 순 정렬 SELECT userID AS '사용자', SUM(price*amount) AS '총구매액', COUNT(*) AS '구매건수' FROM buytbl GROUP BY userID HAVING SUM(price*amount) > 1000 ORDER BY 총구매액 DESC; -- 구매 건수 1건인 회원 (한 번만 구매한 회원 식별) SELECT userID, COUNT(*) AS 구매건수 FROM buytbl GROUP BY userID HAVING COUNT(*) = 1 ORDER BY userID; -- 특정 범위: 총구매액 500 이상 1500 미만 SELECT userID, SUM(price*amount) AS 총구매액 FROM buytbl GROUP BY userID HAVING SUM(price*amount) BETWEEN 500 AND 1499;
HAVING에도 BETWEEN, IN 등 다양한 비교 연산자를 쓸 수 있다. COUNT(*) = 1은 "딱 한 번만 구매한 회원" 같은 특정 그룹 식별에 유용하다.

6 실행 순서 정리 — SQL 처리 단계

SQL은 작성 순서대로 실행되지 않는다. 내부 처리 순서를 알면 WHERE/HAVING/SELECT의 적용 타이밍이 명확해진다.

처리 순서 키워드 역할 가능/불가
1 FROM 테이블 선택
2 WHERE 개별 행 필터 일반 컬럼 ✅ / 집계함수 ❌
3 GROUP BY 그룹 생성
4 집계함수 실행 SUM/COUNT 계산
5 HAVING 그룹 필터 집계함수 ✅ / 일반 컬럼 △
6 SELECT 출력 컬럼 선택 AS 별칭 정의
7 ORDER BY 정렬 AS 별칭 사용 가능
8 LIMIT 출력 행 수 제한
MySQL에서 HAVING이 SELECT의 AS 별칭을 참조할 수 있는 것은 옵티마이저가 처리 순서를 최적화하기 때문이지, 표준 실행 순서상 올바른 것이 아니다. 다른 DBMS에서는 동작하지 않을 수 있다.

7 자주 하는 실수
실수 증상 해결
WHERE에 SUM/COUNT 사용 Invalid use of group function HAVING SUM(...) > N 으로 이동
HAVING만 쓰고 GROUP BY 생략 의미 없는 결과 or 오류 HAVING은 항상 GROUP BY와 함께
HAVING 위치 잘못 Syntax error GROUP BY 다음에 HAVING 위치
일반 컬럼 조건을 HAVING에 넣음 동작은 하지만 느림 WHERE로 이동 — GROUP BY 전에 필터
HAVING에 집계함수 없이 컬럼만 동작은 하지만 혼란 일반 컬럼 조건은 WHERE가 더 적절

핵심 한 줄 요약

HAVINGGROUP BY 결과에 조건 — 집계함수 필터링 전용
WHERE개별 행 필터 — GROUP BY 이전에 실행
집계함수 WHERE 불가WHERE SUM() > N → Invalid use of group function 오류
실행 순서FROM → WHERE → GROUP BY → 집계 → HAVING → SELECT → ORDER BY
HAVING COUNT(*) >= 3구매 건수 3건 이상인 그룹만 출력
HAVING AVG(price) >= 200평균 단가 200 이상인 그룹만 출력
WHERE + HAVING 조합WHERE로 먼저 필터 후 GROUP BY → HAVING — 성능 최적화
MySQL AS 별칭 참조HAVING 총구매액 > 1000 — MySQL 한정 허용 (표준 SQL 비준수)
HAVING 위치GROUP BY 바로 다음 — ORDER BY보다 앞
그룹 필터 전략먼저 WHERE로 범위 줄이기 → GROUP BY → 집계 확인 → HAVING 조건
HAVING SUM > 서브쿼리HAVING SUM() > (SELECT AVG(SUM) ...) — 평균 이상 그룹 필터
집계함수 종류HAVING에 SUM/AVG/COUNT/MAX/MIN 모두 사용 가능
HAVING의 핵심집계 후 필터 — WHERE는 행 단위, HAVING은 그룹 단위
HAVING COUNT(*) = 1딱 한 번만 구매한 회원 — 비활성 고객 분류에 활용
HAVING BETWEENHAVING SUM() BETWEEN 500 AND 1499 — 집계 범위 필터
WHERE 먼저 거르기WHERE로 불필요한 행 제거 → GROUP BY할 데이터 줄어 빠름
HAVING MAX 활용HAVING MAX(price) >= 500 — 그룹 내 최고 단가 조건 필터
HAVING 없는 GROUP BY모든 그룹 출력 — HAVING은 그룹을 추가 필터할 때만 필요
다중 집계 HAVINGHAVING SUM() > 1000 AND COUNT(*) >= 2 — AND/OR 복합 조건
이 파일의 핵심 쿼리GROUP BY userID HAVING SUM(price*amount) > 1000 — 우수 고객 분류
ORDER BY와 조합HAVING 필터 후 ORDER BY로 정렬 — 실무 보고서 완성형
WHERE vs HAVING 한 줄WHERE=집계 전 행 필터, HAVING=집계 후 그룹 필터
실행 순서FROM→WHERE→GROUP BY→집계→HAVING→SELECT→ORDER BY→LIMIT
집계함수 WHERE 오류 메시지Error Code 1111: Invalid use of group function
HAVING IN 사용HAVING userID IN ('AAA','BBB') — 특정 그룹만 (단, WHERE가 적절)
집계 결과 정렬ORDER BY SUM(price*amount) DESC — 집계함수 직접 사용
HAVING NULL 체크HAVING MIN(price) IS NOT NULL — 집계가 NULL인 그룹 제외
GROUP BY 없이 HAVING전체 테이블을 하나의 그룹으로 보고 HAVING 적용 — 비표준 사용
서브쿼리와 HAVINGHAVING SUM() > (SELECT AVG(...) FROM tbl) — 동적 기준값 비교
MySQL HAVING 허용 범위집계함수 + 일반 컬럼 + SELECT AS 별칭 — MySQL 기준
표준 SQL HAVING집계함수와 GROUP BY 컬럼만 허용 — Oracle/PostgreSQL/MSSQL
HAVING 생략 가능GROUP BY 후 모든 그룹을 출력할 때는 HAVING 없어도 됨
성능 팁인덱스가 있는 컬럼은 WHERE → GROUP BY → HAVING 순서가 최적
HAVING = 그룹의 WHEREWHERE가 행을 선택하듯, HAVING은 그룹을 선택하는 역할
그룹 수 확인SELECT COUNT(DISTINCT groupName) — HAVING 적용 전 그룹 수 파악
HAVING이 없는 경우GROUP BY 결과 전체 출력 = HAVING 조건이 항상 TRUE인 것과 동일
WHERE + GROUP BY + HAVING 전형3단 필터: 행 → 그룹 → 그룹 조건 순서로 단계별 좁힘
집계 전 HAVING 불가HAVING은 집계 후 — 집계 전 조건은 반드시 WHERE로 걸어야 함
HAVING OR 조건HAVING SUM()>1000 OR COUNT(*)>=5 — 두 조건 중 하나 만족하는 그룹
NULL 그룹GROUP BY 컬럼에 NULL이 있으면 NULL도 하나의 그룹으로 처리
HAVING vs LIMITHAVING=집계 조건 필터, LIMIT=출력 행 수 제한 — 역할 완전히 다름
HAVING 핵심 규칙집계함수를 조건에 쓰면 → WHERE 아니라 HAVING
buytbl 실습 쿼리GROUP BY userID HAVING SUM(price*amount) > 1000 ORDER BY 총구매액 DESC
WHERE prodName != X특정 상품 제외 후 그룹 집계 — WHERE로 먼저 필터
HAVING 정리GROUP BY 결과를 한번 더 걸러내는 두 번째 필터 관문
HAVING AVG vs WHERE AVGWHERE AVG 오류, HAVING AVG 정상 — 평균 조건은 반드시 HAVING
실무 예월별 매출 합계 1000만 이상인 월만 → GROUP BY 월 HAVING SUM() >= 10000000
HAVING 없어도 되는 경우모든 그룹이 필요하면 HAVING 생략 — GROUP BY만으로 충분
HAVING의 실무 목적우수 고객·저조 상품·빈번 오류 그룹 등 집계 기준 선별
MySQL에서만 되는 것HAVING 별칭 참조, GROUP BY 순서 자유로움 — 다른 DBMS 주의
HAVING COUNT DISTINCTHAVING COUNT(DISTINCT prodName) > 2 — 구매 품목 종류 2개 초과

Tags

#MySQL #HAVING #GROUP BY #WHERE #집계함수 #SUM #조건필터 #SQL #데이터베이스 #웹개발 #티스토리
▼ 티스토리 태그 입력란 복사용
MySQL, HAVING, GROUP BY, WHERE, 집계함수, SUM, 조건필터, SQL, 데이터베이스, 웹개발, 티스토리
반응형

댓글