GROUP BY, HAVING, ORDER BY 결과 읽기
집계 함수, 그룹화, 그룹 조건, 정렬이 결과 행 수와 표시 순서를 어떻게 바꾸는지 시험 풀이 순서로 정리합니다.
GROUP BYHAVINGCOUNTROLLUPCUBE 집계 문제는 행 수 변화부터 봅니다
GROUP BY가 없는 SELECT는 보통 조건을 만족한 행을 그대로 보여줍니다. 하지만 GROUP BY가 있으면 여러 행이 그룹 단위로 묶이고, 결과 행 수가 그룹 수만큼 줄어듭니다.
SELECT deptno, COUNT(*) AS emp_count
FROM emp
GROUP BY deptno;
이 쿼리는 사원 한 명당 한 행이 아니라 부서 하나당 한 행을 반환합니다.
집계 함수의 NULL 처리
| 함수 | NULL 처리 | 비고 |
|---|---|---|
COUNT(*) | NULL 포함, 행 자체를 셉니다 | 유일하게 NULL 포함 |
COUNT(컬럼) | 해당 컬럼이 NULL인 행 제외 | COUNT(*) - COUNT(col) = NULL 개수 |
COUNT(DISTINCT col) | NULL 제외 + 중복 제거 | 고유 값 개수 |
SUM(컬럼) | NULL 제외 | 전부 NULL이면 결과 NULL |
AVG(컬럼) | NULL 제외 후 평균 | 분모도 NULL 제외한 개수 |
MAX, MIN | NULL 제외 | — |
-- COUNT(*) = 14, COUNT(comm) = 3 (comm 있는 사원만)
-- → NULL 인 comm 은 11개
SELECT COUNT(*), COUNT(comm), COUNT(*) - COUNT(comm) AS null_count
FROM emp;
SELECT 절에 올 수 있는 컬럼
GROUP BY를 사용하면 SELECT 절에는 다음 중 하나만 올 수 있습니다.
GROUP BY에 적은 컬럼 (또는 그 표현식)- 집계 함수로 감싼 표현식
- 상수
-- ✗ 잘못된 예: job 이 그룹 기준도 집계도 아님
SELECT deptno, job, COUNT(*)
FROM emp
GROUP BY deptno;
위 쿼리는 job이 그룹 기준도 집계 함수도 아니므로 오류가 발생합니다. 부서별로 여러 직무가 있을 수 있어 DBMS가 어떤 job을 보여줘야 할지 결정할 수 없기 때문입니다.
WHERE와 HAVING 차이
WHERE
- 개별 행을 먼저 거름
- GROUP BY 이전 단계에서 평가
- 집계 함수 사용 불가
- 인덱스 활용 가능
WHERE sal >= 1000
HAVING
- 그룹화된 결과를 거름
- GROUP BY 이후 단계에서 평가
- 집계 함수 사용 가능
- 인덱스 활용 X (이미 집계된 결과)
HAVING AVG(sal) >= 2000
SELECT deptno, AVG(sal) AS avg_sal
FROM emp
WHERE job <> 'PRESIDENT'
GROUP BY deptno
HAVING AVG(sal) >= 2000;
WHERE는 사장 행을 먼저 제거하고, HAVING은 부서별 평균을 계산한 뒤 그룹을 제거합니다. 개별 행 조건은 WHERE에 두어야 처리 대상이 줄어들어 성능에 유리합니다.
ORDER BY
ORDER BY는 최종 결과의 표시 순서를 정합니다.
SELECT deptno, COUNT(*) AS emp_count
FROM emp
GROUP BY deptno
ORDER BY emp_count DESC, deptno ASC;
정렬 기준이 여러 개이면 앞 기준이 같을 때 다음 기준을 사용합니다. 위 쿼리는 사원 수가 많은 부서가 먼저 나오고, 사원 수가 같으면 부서번호가 작은 순서로 표시됩니다.
GROUP BY 확장: ROLLUP · CUBE · GROUPING SETS
| 함수 | 결과 조합 수 (컬럼 n개) | 용도 |
|---|---|---|
GROUP BY ROLLUP(a, b) | n + 1 = 3개: (a,b), (a), () | 계층적 소계 (왼→오) |
GROUP BY CUBE(a, b) | 2^n = 4개: (a,b), (a), (b), () | 모든 조합 소계 |
GROUPING SETS((a,b),(b)) | 명시한 개수만 | 원하는 조합만 |
GROUPING(col) 함수는 결과 행이 해당 컬럼의 소계 행인지(1) 아닌지(0)를 알려줍니다. NULL과 소계-NULL을 구별할 때 사용합니다.
시험 풀이 순서
SELECT 논리 실행 순서
- 1
FROM— 테이블 결합 - 2
WHERE— 개별 행 필터 - 3
GROUP BY— 행을 그룹으로 - 4
HAVING— 그룹 필터 - 5
SELECT— 컬럼 선택 + 별칭 - 6
ORDER BY— 결과 정렬
작성 순서는 SELECT → FROM → WHERE … 이지만 평가는 FROM부터 시작합니다.
WHERE는 행을 줄이고, GROUP BY는 행을 묶고, HAVING은 묶인 결과를 다시 거릅니다.