홈 › SQL 중급 › 10 / 11

집계와 GROUP BY·HAVING

통계 쿼리의 기초
섹션 6진행 0 / 11

2. 핵심 원리

2.1 집계 함수 6종

함수 의미 NULL 처리
COUNT(*) 행 수(모든 행) NULL 포함해서 셈
COUNT(컬럼) 그 컬럼이 NULL 아닌 행 수 NULL 인 행은 제외
COUNT(DISTINCT 컬럼) 중복을 뺀 값의 개수 NULL 인 행은 제외
SUM·AVG 합계·평균 NULL 인 행은 계산에서 제외
MIN·MAX 최솟값·최댓값 NULL 인 행은 계산에서 제외

COUNT(DISTINCT 컬럼) 은 그룹 안에서 서로 다른 값이 몇 종류인지 셀 때 씁니다. 예를 들어 직원별 주문 건수(COUNT(*))와 그 직원이 판매한 서로 다른 고객 수(COUNT(DISTINCT cust))는 다른 질문입니다.

2.2 평가 순서: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY

SQL 문은 위에서 아래로 읽히지만, 실제 처리 순서는 다음과 같습니다.

순서 절 하는 일
1 FROM 테이블을 읽고 조인한다
2 WHERE 그룹으로 묶기 전에 개별 행을 거른다
3 GROUP BY 남은 행을 기준 컬럼으로 그룹 짓는다
4 HAVING 만들어진 그룹을 집계 값 기준으로 거른다
5 SELECT 출력할 컬럼·별칭을 계산한다
6 ORDER BY 최종 결과를 정렬한다
핵심

WHERE 는 그룹이 만들어지기 전에 개별 행 단위로 거르므로 집계 함수를 쓸 수 없습니다. HAVING 은 그룹이 만들어진 뒤에 그룹 단위로 거르므로 집계 함수를 써야 의미가 있습니다. "WHERE 는 행, HAVING 은 그룹" 이 기준입니다.

WHERE 로 걸러낸 행은 애초에 그룹 집계에 들어가지 않고, HAVING 으로 걸러낸 그룹은 집계까지 계산된 뒤 결과에서만 빠집니다. 같은 조건이라도 어느 단계에서 거르느냐에 따라 집계 값 자체가 달라질 수 있어, 3절에서 두 절을 각각·함께 쓴 결과를 비교합니다.

2.3 SELECT 의 비집계 컬럼은 GROUP BY 에 있어야 한다

GROUP BY dept 로 묶은 쿼리에서 SELECT id, dept, COUNT(*) 처럼 그룹 기준에 없는 id 를 그대로 쓰면 안 됩니다. 한 그룹 안에는 여러 id 값이 섞여 있어 어느 것을 보여줘야 할지 정해지지 않기 때문입니다.

주의

Oracle 은 ORA-00979, MSSQL 은 오류 8120 으로 이 문장을 바로 거부합니다. MySQL 은 5.7.5 부터 ONLY_FULL_GROUP_BY 가 기본으로 켜져 있어 역시 오류가 나지만, 이 설정을 끈 예전 환경에서는 오류 없이 그룹 안의 임의의 행 값을 돌려주는 위험한 동작을 합니다.

2.4 GROUP BY 없는 HAVING, 그리고 빈 테이블

GROUP BY 없이 HAVING 만 쓰면 테이블 전체가 하나의 그룹이 됩니다. 조건을 만족하면 집계 결과 1행, 만족하지 않으면 0행이 나옵니다.

빈 테이블(행이 0개)의 집계는 GROUP BY 유무에 따라 결과 행 수가 달라집니다. GROUP BY 없이 COUNT(*)·SUM 을 조회하면 "그룹 1개(전체), 그 그룹에 행이 0개"로 취급되어 COUNT(*) 는 0, SUM 은 NULL 인 1행이 나옵니다. GROUP BY 가 있으면 애초에 나눌 행이 없으므로 0행입니다.

주의

빈 테이블에서 GROUP BY 없는 SELECT COUNT(*) FROM t 는 항상 1행(0)을 돌려주지만, GROUP BY 컬럼 이 붙으면 0행입니다. "집계 쿼리는 결과가 항상 1행 이상"이라고 가정하고 코드를 짜면, GROUP BY 가 섞인 리포트에서 결과가 없는 경우를 놓치기 쉽습니다.