HAVING 절은 SQL에서 그룹화된 데이터에 대한 필터링을 수행하는 데 사용된다. 이는 집계 함수와 함께 동작하며, 개별 행을 필터링하는 WHERE 절과는 다르다. HAVING은 그룹화된 데이터에 조건을 적용할 때 유용하며, 주로 GROUP BY 절과 함께 사용된다.
집계 함수에 대한 내용은 여기를 참고하세요.
SUM, AVG, COUNT, MAX, MIN 등과 같은 집계 함수의 결과를 기준으로 데이터를 필터링한다.HAVING 절은 그룹화(GROUP BY)가 완료된 후 실행되며, ORDER BY 절보다 앞에 위치한다.WHERE과 비슷해보이지만 다르다.WHERE: 집계 이전의 개별 행을 필터링 (전체 데이터에서 먼저 필터링 후 집계 함수 적용)HAVING: 집계 이후의 그룹 데이터를 필터링 (전체 데이터에서 집계 함수가 먼저 돌아간 뒤 필터링)WHERE은 전체 데이터에서 먼저 필터링을 진행하고 집계를 하기 때문에 필터링으로 줄어든 데이터 개수로 인한 성능상 이점이 있다. 하지만 HAVING의 경우에는 전체 데이터에서 먼저 집계를 하기 때문에 상대적으로 WHERE보다 성능이 안좋다.
그럼 언제 HAVING을 사용해야 할까?
집계 이전에 필터링할 수 있는 데이터는 반드시 WHERE 절에서 처리한다. 앞에서도 말했지만 이렇게 하면 불필요한 데이터 처리를 줄이고 성능을 향상시킬 수 있다. (=어차피 필터링되는 데이터를 굳이 집계하고 난 뒤 필터링하기 때문)
SELECT Department, SUM(Salary) AS TotalSalary
FROM Employee
WHERE Salary > 3000 -- 개별 행 필터링
GROUP BY Department
HAVING SUM(Salary) > 50000; -- 그룹 필터링
복잡한 조건보다는 명확하고 간결한 조건을 사용하는 것이 가독성과 유지보수성에 좋다. 특히 중첩 집계 함수 사용을 최소화하는 것이 좋다.
중첩 집계 함수의 사용 가능 여부 - (
예: MAX(SUM(col)))
SQL 표준 및 RDB에 따라 다르다.SQL 표준에서는 중첩 집계 함수의 사용을 명시적으로 금지하진 않는다. 하지만 대부분의 데이터베이스 시스템에서는 이를 지원하지 않는다. 이에 대한 대안으로 서브쿼리 사용이 있다.
# Wrong Practice
SELECT department, SUM(salary) AS total_salary
FROM employees
GROUP BY department
HAVING SUM(salary) > 50000 AND AVG(salary) > (SELECT AVG(salary) FROM employees);
# Best Practice - 불필요한 평균 비교를 제거하여 가독성과 성능을 개선
SELECT department, SUM(salary) AS total_salary
FROM employees
GROUP BY department
HAVING SUM(salary) > 50000;
집계 함수의 반복 계산을 피하기 위해 결과를 재사용한다.
# Wrong Practice
SELECT department, SUM(salary) AS total_salary
FROM employees
GROUP BY department
HAVING SUM(salary) > 50000 AND SUM(salary) < 100000;
# Best Practice - `SUM(salary)`를 한 번만 계산하여 성능 최적화
SELECT department, total_salary
FROM (
SELECT department, SUM(salary) AS total_salary
FROM employees
GROUP BY department
) AS subquery
WHERE total_salary BETWEEN 50000 AND 100000;
HAVING 절의 조건을 줄이기 위해 WHERE 절을 적극 활용한다.
# Wrong Practice
SELECT department, SUM(salary) AS total_salary
FROM employees
GROUP BY department
HAVING COUNT(*) > 10;
# Best Practice - `WHERE` 절을 통해 그룹화 이전에 데이터를 미리 필터링하여 처리량 감소
SELECT department, SUM(salary) AS total_salary
FROM employees
WHERE salary > 3000 -- 개별 행 필터링으로 데이터 양 축소
GROUP BY department
HAVING COUNT(*) > 10;