이 글의 핵심
WHERE는 그룹화 전의 행을, HAVING은 만들어진 그룹을 필터링합니다.
GROUP BY 이후 SELECT에는 그룹 기준 열이나 집계 표현식을 사용해야 합니다.
COUNT(*)와 COUNT(열)은 NULL 포함 여부가 다릅니다.
WHERE와 HAVING은 필터 대상이 다르다
WHERE는 개별 행을 대상으로 하며 GROUP BY보다 먼저 실행됩니다. HAVING은 GROUP BY로 생성된 그룹을 대상으로 하므로 SUM, COUNT 같은 집계 결과를 조건에 사용할 수 있습니다.
“2026년 주문만 모아 고객별 합계를 구한 뒤 합계가 100만 원 이상인 고객”을 찾는다면 연도 조건은 WHERE, 합계 조건은 HAVING에 둡니다.
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
WHERE order_date >= DATE '2026-01-01'
GROUP BY customer_id
HAVING SUM(amount) >= 1000000;논리적 실행 순서로 오류 찾기
SQL을 작성하는 순서와 논리적으로 처리되는 순서는 다릅니다. SELECT에서 만든 별칭을 WHERE에서 바로 사용할 수 없는 이유도 WHERE가 SELECT보다 먼저 처리되기 때문입니다.
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BYGROUP BY 이후 행의 의미가 바뀐다
GROUP BY 전에는 한 행이 주문 한 건을 의미하지만, 고객별 GROUP BY 후에는 한 행이 고객 한 명의 주문 묶음을 의미합니다. 따라서 SELECT에는 customer_id처럼 그룹을 대표하는 열이나 SUM(amount)처럼 그룹 전체를 계산한 값이 와야 합니다.
그룹에 포함되지 않은 일반 열을 SELECT에 그대로 쓰면 어떤 행의 값을 보여줘야 할지 결정할 수 없습니다. DBMS 설정에 따라 오류가 나거나 비결정적인 결과를 만들 수 있으므로 시험에서는 잘못된 문장으로 판단합니다.
COUNT와 NULL을 함께 확인하기
COUNT(*)는 그룹의 모든 행을 세지만 COUNT(column)은 해당 열이 NULL이 아닌 행만 셉니다. COUNT(DISTINCT column)은 NULL을 제외하고 서로 다른 값의 개수를 셉니다.
| 표현식 | 세는 대상 |
|---|---|
| COUNT(*) | 모든 행 |
| COUNT(score) | score가 NULL이 아닌 행 |
| COUNT(DISTINCT score) | NULL을 제외한 서로 다른 score 값 |
집계 문제 점검표
결과를 계산하기 전에 WHERE로 제거되는 행에 선을 긋고, 남은 행을 GROUP BY 값별로 묶으세요. 각 묶음에서 집계값을 계산한 다음 HAVING을 적용하면 실수가 줄어듭니다.
- 집계 전 필터인가, 집계 후 필터인가?
- NULL이 집계에 포함되는가?
- GROUP BY 열 조합이 실제 몇 개의 그룹을 만드는가?
- DISTINCT가 행 전체인지 특정 집계 인수에 적용되는지 확인했는가?
자주 묻는 질문
GROUP BY 없이 HAVING을 사용할 수 있나요?
DBMS에 따라 전체 결과를 하나의 그룹으로 보고 사용할 수 있습니다. 다만 SQLD 문제에서는 구문과 DBMS별 동작 조건을 함께 확인해야 합니다.
ORDER BY에서 SELECT 별칭을 사용할 수 있는 이유는 무엇인가요?
논리적 처리 순서에서 ORDER BY가 SELECT 이후에 평가되므로 SELECT에서 정한 별칭을 참조할 수 있습니다.
시험 과목과 합격 기준은 KDATA SQL 개발자 공식 안내를 기준으로 확인했습니다. 가풀은 시험 주관기관의 공식 서비스가 아니며, 일정과 규정은 접수 전 공식 공고를 다시 확인해야 합니다.