SQL 그룹 함수
IT위키
더 많은 작업
- 상위 문서: SQL
- Group Function
- 여러 행을 그룹으로 묶어 하나의 결과를 돌려주는 함수와, 소계·총계를 함께 만드는 GROUP BY 확장 기능
| 함수 | 설명 |
|---|---|
| COUNT(*) / COUNT(컬럼) | 행 수. COUNT(컬럼)은 NULL을 세지 않는다. |
| SUM, AVG | 합계, 평균(NULL 제외) |
| MAX, MIN | 최댓값, 최솟값 |
| STDDEV, VARIANCE | 표준편차, 분산(Oracle 함수명. 표준 SQL은 STDDEV_SAMP, VAR_SAMP 등) |
- COUNT(*)를 제외한 집계 함수는 NULL을 무시한다.
- COUNT 의 세 형태는 세는 대상이 다르다.[1]
COUNT(*)- 입력 투플(행)의 수. 처리 대상이 투플의 집합이며, 값이 같은 중복 투플도 각각 센다COUNT(칼럼)- 그 칼럼이 NULL 이 아닌 행의 수. 중복 값도 센다COUNT(DISTINCT 칼럼)- NULL 이 아닌 서로 다른 값의 수
- 그래서 중복 값이 있는 칼럼은 COUNT(칼럼) 과 COUNT(DISTINCT 칼럼) 이 다르다(예: 값 a, a, b 이면 3 과 2). 중복 값이 없는 칼럼이라도 COUNT(*) 와 COUNT(칼럼) 이 달라지는 것은 그 칼럼에 NULL 이 있을 때뿐이고, NULL 이 없으면 둘은 같다
SELECT 부서, COUNT(*) AS 인원, AVG(급여) AS 평균급여
FROM 사원
WHERE 입사년도 >= 2020 -- 그룹을 만들기 전에 행을 거른다
GROUP BY 부서
HAVING COUNT(*) >= 3; -- 그룹을 만든 뒤 그룹을 거른다
- 처리 순서: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
- 집계 함수 조건은 WHERE에 쓸 수 없고 HAVING에 쓴다.
- GROUP BY를 쓰면 SELECT 절에는 GROUP BY 컬럼과 집계 함수만 올 수 있다.
- GROUP BY 컬럼의 NULL 값들은 하나의 그룹으로 묶인다. 소속팀이 L 10명, H 40명, K 30명, NULL 20명이면
GROUP BY 소속팀결과는 네 그룹이고,HAVING COUNT(*) >= 20을 붙이면 H, K, NULL 세 그룹이 남는다 - HAVING 조건이 NULL(알 수 없음)이 되는 그룹은 걸러진다. 고객 테이블을 주문 테이블과 LEFT JOIN 해 고객별 SUM(금액)을 구하면 주문이 없는 고객의 합계는 0 이 아니라 NULL 이다. 여기에
HAVING SUM(금액) >= 0을 붙이면 NULL >= 0 이 참이 아니어서 주문 없는 고객이 결과에서 빠진다. 같은 합계를 스칼라 서브쿼리나 미리 집계한 인라인 뷰를 LEFT JOIN 해서 구하면 그 고객은 NULL 합계로 남는다 - HAVING 에서 SELECT 의 별칭을 쓰는 것(
HAVING total >= 0)은 MySQL 은 허용하지만 표준 SQL 과 Oracle 은 허용하지 않아 집계식을 다시 써야 한다
판매 테이블
| 지역 | 제품 | 수량 |
|---|---|---|
| 서울 | A | 10 |
| 서울 | B | 20 |
| 부산 | A | 30 |
나열한 컬럼의 오른쪽부터 하나씩 빼며 계층별 소계를 만든다. n개 컬럼이면 n+1단계의 그룹이 생기며, 컬럼 순서가 바뀌면 결과도 바뀐다.
SELECT 지역, 제품, SUM(수량) AS 합계
FROM 판매
GROUP BY ROLLUP(지역, 제품)
ORDER BY 지역, 제품;
나열한 컬럼의 모든 조합으로 소계를 만든다. n개 컬럼이면 2n가지 그룹이 생긴다.
SELECT 지역, 제품, SUM(수량) AS 합계
FROM 판매
GROUP BY CUBE(지역, 제품)
ORDER BY 지역, 제품;
| 지역 | 제품 | 합계 | 그룹 기준 | ROLLUP | CUBE |
|---|---|---|---|---|---|
| 부산 | A | 30 | (지역, 제품) | ○ | ○ |
| 부산 | NULL | 30 | (지역) 소계 | ○ | ○ |
| 서울 | A | 10 | (지역, 제품) | ○ | ○ |
| 서울 | B | 20 | (지역, 제품) | ○ | ○ |
| 서울 | NULL | 30 | (지역) 소계 | ○ | ○ |
| NULL | A | 40 | (제품) 소계 | ○ | |
| NULL | B | 20 | (제품) 소계 | ○ | |
| NULL | NULL | 60 | 총계 | ○ | ○ |
- ROLLUP은 6행, CUBE는 8행이다. CUBE만 제품별 소계(A 40, B 20)를 더 만든다.
- 표의 정렬은 Oracle 기준이다. Oracle은 오름차순 정렬에서 NULL을 맨 뒤에 둔다.
원하는 그룹 기준만 골라 각각 집계한 뒤 합친다(UNION ALL과 같은 효과).
SELECT 지역, 제품, SUM(수량) AS 합계
FROM 판매
GROUP BY GROUPING SETS((지역), (제품));
-- 결과: (부산, NULL, 30), (서울, NULL, 30), (NULL, A, 40), (NULL, B, 20)
ROLLUP(지역, 제품)=GROUPING SETS((지역, 제품), (지역), ())CUBE(지역, 제품)=GROUPING SETS((지역, 제품), (지역), (제품), ())
소계·총계를 만들면서 생긴 NULL이면 1, 실제 데이터 값이면 0을 돌려준다. 원래 데이터의 NULL과 소계 행을 구분할 때 쓴다.
SELECT CASE GROUPING(지역) WHEN 1 THEN '전체' ELSE 지역 END AS 지역,
CASE GROUPING(제품) WHEN 1 THEN '소계' ELSE 제품 END AS 제품,
SUM(수량) AS 합계
FROM 판매
GROUP BY ROLLUP(지역, 제품);
- MySQL은
GROUP BY 지역, 제품 WITH ROLLUP문법을 쓰며, CUBE와 GROUPING SETS는 지원하지 않는다. GROUPING 함수는 8.0부터 지원한다.
- 실기 SQL: GROUP BY와 HAVING으로 조건에 맞는 그룹을 조회하는 문장 작성
- ROLLUP과 CUBE의 결과 행 수 차이(n+1단계, 2n가지)와 소계 행의 NULL
- 필기: WHERE와 HAVING의 차이, COUNT(*)와 COUNT(컬럼)의 NULL 처리 차이
- ↑ 「4.2.7 Aggregate Expressions」, PostgreSQL Documentation, https://www.postgresql.org/docs/current/sql-expressions.html, 확인일: 2026-11-05.