본문으로 건너뛰기

GROUP BY와 집계 함수

SQLD 목차SQL 기본레슨 17 / 35

목차

GROUP BY는 같은 값을 가진 행을 한 그룹으로 묶습니다. 집계 함수는 그룹마다 값을 하나씩 냅니다.

"부서마다 사원이 몇 명인가?"는 행 하나가 아니라 여러 행을 묶어야 답할 수 있는 질문입니다. 행을 묶는 일은 GROUP BY 절이 맡고, 묶인 행들을 개수·합계·평균 같은 값 하나로 줄이는 일은 집계 함수(aggregate function)가 맡습니다.

7단계 중 1단계. EMP 6행 가운데 정하늘(1005)만 DEPTNO가 NULL입니다.

1같은 값끼리 묶기와 NULL 그룹

GROUP BY DEPTNO는 DEPTNO 값이 같은 행을 한 그룹에 넣습니다. 10번과 30번에는 1행씩, 20번에는 3행이 들어갑니다. 정하늘처럼 DEPTNO가 NULL인 행도 빠지지 않고, NULL끼리 모여 그룹 하나를 따로 이룹니다. 그래서 EMP는 그룹이 4개입니다. 조건식에서는 NULL = NULL이 참이 아니지만, 묶을 때는 NULL끼리 같은 그룹으로 칩니다. GROUP BY를 쓰지 않으면 테이블 전체가 한 그룹입니다.

2집계 함수와 NULL

집계 함수는 그룹 안의 값을 모아 하나로 줄입니다. COUNT(*)만 행을 세고, 나머지는 NULL인 칸을 건너뜁니다.

함수NULL인 칸을NULL 그룹
COUNT(*)셈(행을 셈)1
COUNT(DEPTNO)건너뜀0
SUM(SAL)건너뜀300
AVG(SAL)건너뛰고 남은 수로 나눔300

MAX·MIN도 NULL을 건너뜁니다. COUNT(DISTINCT 열)은 NULL을 뺀 뒤 같은 값을 한 번만 셉니다. 걸러서 남은 행이 하나도 없어도 GROUP BY가 없으면 그 한 그룹이 남아 1행이 나옵니다. 이때 COUNT(*)는 0, SUM·AVG는 NULL입니다. GROUP BY가 있으면 묶을 행이 없어 0행입니다.

3SELECT에 올 수 있는 것

그룹 하나가 결과 한 줄이 되므로, SELECT에는 그룹마다 값이 하나로 정해지는 것만 올 수 있습니다. GROUP BY에 쓴 열, 집계 함수, 상수와 이것들로 만든 식입니다. 20번 그룹은 ENAME이 셋이라, GROUP BY에 없는 ENAME을 그대로 쓰면 Oracle은 ORA-00979 오류를 냅니다. HAVING 절(묶은 그룹을 조건으로 거르는 절 — HAVING 절 레슨에서 자세히)과 ORDER BY에도 같은 제한이 걸립니다. GROUP BY 없이 집계 함수와 일반 열을 함께 써도 한 줄에 담을 수 없어 ORA-00937 오류입니다.

SELECT ENAME,
  COUNT(*)
FROM EMP;

예제

그림의 쿼리입니다. 결과를 DEPTNO 순서로 보려고 ORDER BY DEPTNO를 붙였습니다.

SELECT DEPTNO,
  COUNT(*),
  COUNT(DEPTNO),
  SUM(SAL),
  AVG(SAL)
FROM EMP
GROUP BY DEPTNO
ORDER BY DEPTNO;
DEPTNOCOUNT(*)COUNT(DEPTNO)SUM(SAL)AVG(SAL)
1011520520
20331260420
3011450450
NULL10300300

NULL 그룹이 맨 아래인 것은 Oracle이 오름차순(작은 값부터 세우는 정렬)에서 NULL을 가장 큰 값으로 보기 때문입니다. SQL Server는 맨 위에 둡니다(ORDER BY 절에서 자세히).

GROUP BY 없이 집계하면 EMP 전체가 한 줄로 줄어듭니다.

SELECT COUNT(*),
  COUNT(DEPTNO)
FROM EMP;
COUNT(*)COUNT(DEPTNO)
65

시험 포인트

  • COUNT(*)는 NULL과 상관없이 행을 세고, COUNT(열)은 그 열이 NULL인 행을 빼고 셉니다.
  • SUM·AVG·MAX·MIN도 NULL을 빼고 계산합니다. AVG(열)은 NULL이 아닌 값들만의 평균입니다.
  • COUNT(DISTINCT 열)은 NULL을 빼고, 같은 값은 한 번만 셉니다.
  • GROUP BY 열이 NULL인 행들은 NULL 그룹 하나로 모입니다.
  • 남은 행이 없으면 GROUP BY가 없을 때는 COUNT(*) 0 · SUM NULL 한 줄, 있을 때는 0행입니다.
  • GROUP BY를 쓴 쿼리의 SELECT·HAVING·ORDER BY에는 GROUP BY 열, 집계 함수, 상수와 그 식만 옵니다(ORA-00979).

확인 문제

1. 다음 쿼리의 결과는 몇 줄일까요? NULL 그룹이 있을까요?

SELECT DEPTNO,
  COUNT(*)
FROM EMP
WHERE SAL >= 400
GROUP BY DEPTNO
ORDER BY DEPTNO;
정답

3줄이고, NULL 그룹은 없습니다. WHERE가 묶기 전에 SAL이 400보다 작은 이서연(380)과 정하늘(300)을 걸러 냅니다. DEPTNO가 NULL인 행은 정하늘 하나뿐이라, 정하늘이 빠지면 NULL 그룹도 생기지 않습니다.

DEPTNOCOUNT(*)
101
202
301

2. 다음 쿼리의 결과 세 값은 무엇일까요?

SELECT COUNT(*),
  COUNT(DEPTNO),
  COUNT(DISTINCT DEPTNO)
FROM EMP;
정답

6, 5, 3입니다.

COUNT(*)COUNT(DEPTNO)COUNT(DISTINCTDEPTNO)
653

표 머리의 COUNT(DISTINCTDEPTNO)⁠처럼, Oracle은 식으로 만든 열 이름에서 공백을 뺍니다.

  • COUNT(*)는 6행을 모두 셉니다.
  • COUNT(DEPTNO)는 DEPTNO가 NULL인 정하늘을 빼고 5행을 셉니다.
  • COUNT(DISTINCT DEPTNO)⁠는 NULL을 뺀 10, 20, 20, 30, 20에서 서로 다른 값 10, 20, 30만 세어 3입니다.

3. 다음 쿼리는 ENAME 때문에 Oracle에서 오류가 납니다. GROUP BY 줄을 GROUP BY DEPTNO, ENAME⁠으로 바꾸면 결과는 몇 줄이고, 부서별 월급 합계라는 처음 뜻과 무엇이 달라질까요?

SELECT DEPTNO,
  ENAME,
  SUM(SAL)
FROM EMP
GROUP BY DEPTNO;
정답

6줄입니다. 오류는 없어지지만 부서별 합계가 아니라 사원마다 한 줄이 됩니다.

SELECT DEPTNO,
  ENAME,
  SUM(SAL)
FROM EMP
GROUP BY DEPTNO, ENAME
ORDER BY DEPTNO, ENAME;
DEPTNOENAMESUM(SAL)
10김민준520
20강도윤470
20박지호410
20이서연380
30최유나450
NULL정하늘300

DEPTNO와 ENAME이 둘 다 같아야 한 그룹인데, 같은 부서에 이름이 같은 사원이 없어 그룹이 6개 생깁니다. 그래서 SUM(SAL)은 20번의 1260이 아니라 사원 한 명의 월급입니다. 부서별 합계를 보려면 ENAME을 SELECT에서 빼야 합니다.

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

SQLD 목차로