•
5대 집계함수 COUNT · SUM · AVG · MAX · MIN 을 사용할 수 있다.
•
COUNT(*) · COUNT(컬럼) · COUNT(DISTINCT 컬럼) 의 차이를 설명할 수 있다.
•
AVG 가 NULL을 분모에서 제외한다는 것을 계산으로 증명할 수 있다.
•
GROUP BY 로 데이터를 묶고, SELECT 절 작성 규칙을 지킬 수 있다.
•
WHERE 와 HAVING 의 차이를 실행 순서로 설명할 수 있다.
1. 집계함수 개요
1) 여러 행을 하나로 묶는다
구분 | 단일행 함수 (09·10강) | 집계함수(다중행 함수) |
입력 → 출력 | N행 → N행 | N행 → 1행 |
예 | UPPER(ENAME) | SUM(SAL) |
EMP 14건에 적용 | 14건 출력 | 1건 출력 |
2) 5대 집계함수
함수 | 기능 | 대상 타입 |
COUNT | 행의 개수 | 모든 타입 |
SUM | 합계 | 숫자만 |
AVG | 평균 | 숫자만 |
MAX | 최댓값 | 숫자·문자·날짜 |
MIN | 최솟값 | 숫자·문자·날짜 |
MAX MIN 은 문자·날짜에도 됩니다. 문자는 사전 순, 날짜는 시간 순으로 비교합니다.2. 집계함수와 NULL — "무시" 가 대원칙 


1) 집계함수는 NULL을 계산에서 제외한다
-- ============================================================
-- 【시험용 예제】 EMP 의 커미션(COMM) 으로 5대 함수를 모두 적용 ★★
-- COMM 은 14명 중 4명만 값이 있고, 나머지 10명은 NULL 이다.
-- ============================================================
SELECT COUNT(*) AS "행 개수", -- 전체 행 수
COUNT(COMM) AS "COMM 개수", -- COMM 이 NULL이 아닌 행 수
SUM(COMM) AS "COMM 합계",
AVG(COMM) AS "COMM 평균",
MAX(COMM) AS "COMM 최대",
MIN(COMM) AS "COMM 최소"
FROM EMP;
-- 행 개수 COMM 개수 COMM 합계 COMM 평균 COMM 최대 COMM 최소
-- ------- --------- --------- --------- --------- ---------
-- 14 4 2200 550 1400 0
-- ↑ ↑
-- NULL 10건은 세지 않음 2200 ÷ 4 = 550 (NULL 제외!)
--
-- ⭐ 핵심 : SUM, AVG, MAX, MIN, COUNT(컬럼) 은 모두 NULL 을 '무시' 한다.
-- 유일한 예외가 COUNT(*) 이다 (다음 절).
SQL
복사
커미션을 받는 4명만 뽑아보면 계산이 확인됩니다
사원 | COMM |
ALLEN | 300 |
WARD | 500 |
MARTIN | 1400 |
TURNER | 0 ← 0도 값이므로 포함 |
합계 | 2200 |
개수 | 4 |
평균 | 2200 ÷ 4 = 550 |
TURNER 의 커미션 0 은 NULL이 아니므로 계산에 포함됩니다.
NULL인 나머지 10명만 빠집니다.3. COUNT 의 세 가지 얼굴 


-- ============================================================
-- 【시험용 예제】 COUNT 세 가지의 결과가 모두 다르다 ★★★
-- ============================================================
SELECT COUNT(*) AS "전체 행", -- 14
COUNT(COMM) AS "COMM 있는 행", -- 4 (NULL 제외)
COUNT(DISTINCT DEPTNO) AS "부서 종류", -- 3 (10,20,30)
COUNT(DISTINCT JOB) AS "직무 종류" -- 5
FROM EMP;
-- 전체 행 COMM 있는 행 부서 종류 직무 종류
-- ------- ------------ --------- ---------
-- 14 4 3 5
--
-- ⭐ 정리
-- COUNT(*) → 무조건 전체 행 (NULL 신경 안 씀)
-- COUNT(COMM) → COMM 이 있는 행만 = 14 - (NULL 10건) = 4
-- COUNT(DISTINCT DEPTNO)→ 서로 다른 부서번호의 개수 (40번은 사원이 없어 안 나옴)
SQL
복사
표현 | 세는 대상 | NULL |
COUNT(*) | 모든 행 | 포함 ![]() |
COUNT(COMM) | COMM이 NULL이 아닌 행 | 제외 |
COUNT(DISTINCT COMM) | COMM의 서로 다른 값 | 제외 |
COUNT(*) 만 NULL을 셉니다. 나머지는 전부 NULL을 제외합니다.
"행이 몇 개인가"를 물으면 COUNT(*), "값이 몇 개 있나"를 물으면 COUNT(컬럼) 입니다.4. AVG 와 NULL 분모 함정 


이 단원 최고 빈출 문제입니다. 분모가 몇이냐로 답이 완전히 갈립니다.1) AVG(COMM) 은 14가 아니라 4로 나눈다
-- ============================================================
-- 【시험용 예제】 같은 데이터, 다른 분모 → 다른 답 ★★★
-- ============================================================
SELECT AVG(COMM) AS "AVG(COMM)", -- NULL 제외 → 분모 4
AVG(NVL(COMM, 0)) AS "AVG(NVL(COMM,0))", -- NULL을 0으로 → 분모 14
SUM(COMM) AS "SUM", -- 2200 (둘 다 동일)
SUM(COMM) / COUNT(*) AS "SUM/COUNT(*)", -- 2200 ÷ 14
SUM(COMM) / COUNT(COMM) AS "SUM/COUNT(COMM)" -- 2200 ÷ 4
FROM EMP;
-- AVG(COMM) AVG(NVL(COMM,0)) SUM SUM/COUNT(*) SUM/COUNT(COMM)
-- --------- ---------------- ---- ------------ ---------------
-- 550 157 2200 157 550
--
-- ⭐ 결론
-- AVG(COMM) = SUM(COMM) / COUNT(COMM) ← 분모 4
-- AVG(NVL(COMM,0)) = SUM(COMM) / COUNT(*) ← 분모 14
-- 분자(SUM)는 같지만 분모가 다르다!
SQL
복사
계산식 | 분자 | 분모 | 결과 |
AVG(COMM) | 2200 | 4 (NULL 제외) | 550 |
AVG(NVL(COMM, 0)) | 2200 | 14 (NULL→0으로 참여) | ≈157 |
SUM(COMM) / COUNT(*) | 2200 | 14 | ≈157 |
SUM(COMM) / COUNT(COMM) | 2200 | 4 | 550 |
시험 함정: "커미션의 평균을 구하라"고 할 때•
순수 평균 (커미션 받는 사람 기준) → AVG(COMM) = 550
•
전체 사원 기준 평균 (안 받는 사람을 0으로) → AVG(NVL(COMM,0)) ≈ 157
문제가 어느 쪽을 원하는지 반드시 확인해야 합니다.
5. GROUP BY — 그룹으로 묶기 

1) 부서별로 묶어서 집계
-- ============================================================
-- 【이해용 예제】 부서별 인원수와 급여 합계
-- ============================================================
SELECT DEPTNO AS 부서번호,
COUNT(*) AS 인원수, -- 그룹마다 행 수를 센다
SUM(SAL) AS 급여합계,
ROUND(AVG(SAL)) AS 평균급여
FROM EMP
GROUP BY DEPTNO -- DEPTNO 값이 같은 행끼리 묶는다
ORDER BY DEPTNO;
-- 부서번호 인원수 급여합계 평균급여
-- -------- ------ -------- --------
-- 10 3 8750 2917
-- 20 5 10875 2175
-- 30 6 9400 1567
--
-- ⭐ GROUP BY DEPTNO → 부서 3개 그룹 → 결과 3행 (14행이 3행으로 압축됨)
SQL
복사
2)
SELECT 절의 철칙 


GROUP BY를 쓰면, SELECT 에는 ①GROUP BY 컬럼 또는 ②집계함수만 올 수 있다.
-- ============================================================
-- 【시험용 예제】 그룹 컬럼이 아닌 일반 컬럼은 SELECT 에 못 온다 ★★
-- ============================================================
-- ❌ 오류: ENAME 은 GROUP BY 에도 없고 집계함수도 아니다
SELECT DEPTNO, ENAME, SUM(SAL)
FROM EMP
GROUP BY DEPTNO;
-- ORA-00979: not a GROUP BY expression
-- → 한 부서에 사원이 여러 명인데 ENAME 을 무엇으로 보여줘야 할지 알 수 없다!
-- (10번 그룹의 ENAME 은 CLARK? KING? MILLER? → 결정 불가)
-- ✅ 해결 ① ENAME 을 빼거나
SELECT DEPTNO, SUM(SAL) FROM EMP GROUP BY DEPTNO;
-- ✅ 해결 ② ENAME 도 GROUP BY 에 넣는다 (그러면 그룹 기준이 달라진다)
SELECT DEPTNO, ENAME, SAL FROM EMP GROUP BY DEPTNO, ENAME, SAL;
SQL
복사
왜 안 되는가? 한 그룹은 여러 행이 뭉친 것인데, 일반 컬럼은 행마다 값이 달라 하나로 표시할 수 없기 때문입니다. 반면 집계함수는 그룹을 하나의 값으로 요약하므로 괜찮습니다.3) GROUP BY 컬럼은 SELECT에 없어도 된다
-- SELECT 에 DEPTNO 가 없어도 GROUP BY 는 정상 동작한다 (반대는 불가)
SELECT SUM(SAL) AS 급여합계
FROM EMP
GROUP BY DEPTNO; -- 부서별로 묶지만, 어느 부서인지는 표시하지 않음
-- 급여합계
-- --------
-- 8750
-- 10875
-- 9400 → 3행 (어느 부서인지 알 수 없어 실용성은 낮다)
SQL
복사
6. HAVING — 그룹을 걸러내기 


1) WHERE 로는 집계함수를 조건에 쓸 수 없다
-- ============================================================
-- 【시험용 예제】 "급여합계가 9000 초과인 부서" 를 찾을 때 ★★★
-- ============================================================
-- ❌ WHERE 에 집계함수를 쓸 수 없다
SELECT DEPTNO, SUM(SAL)
FROM EMP
WHERE SUM(SAL) > 9000 -- ORA-00934: group function is not allowed here
GROUP BY DEPTNO;
-- → WHERE 는 '그룹으로 묶기 전' 에 실행되므로, 아직 SUM(SAL) 이 존재하지 않는다!
-- ✅ 그룹에 대한 조건은 HAVING 을 쓴다
SELECT DEPTNO, SUM(SAL) AS 급여합계
FROM EMP
GROUP BY DEPTNO
HAVING SUM(SAL) > 9000; -- 그룹핑 후, 합계로 그룹을 거른다
-- DEPTNO 급여합계
-- ------ --------
-- 20 10875
-- 30 9400 → 10번(8750)은 9000 이하라 제외됨
SQL
복사
2) WHERE vs HAVING — 실행 순서로 이해 

WHERE | HAVING | |
실행 시점 | 그룹핑 전 | 그룹핑 후 |
필터 대상 | 개별 행 | 그룹 |
집계함수 | 쓸 수 없음 | 쓸 수 있음 |
예 | WHERE SAL > 1000 | HAVING SUM(SAL) > 9000 |
-- ============================================================
-- 【시험용 예제】 WHERE 와 HAVING 을 함께 쓰기 ★★
-- "급여 1000 이상인 사원들만 대상으로, 부서별 인원이 3명 이상인 부서"
-- ============================================================
SELECT DEPTNO,
COUNT(*) AS 인원수,
SUM(SAL) AS 급여합계
FROM EMP
WHERE SAL >= 1000 -- ① 먼저 급여 1000 미만인 개별 행을 제거 (SMITH 800, JAMES 950 탈락)
GROUP BY DEPTNO -- ② 남은 행을 부서별로 묶고
HAVING COUNT(*) >= 3 -- ③ 인원이 3명 이상인 그룹만 남긴다
ORDER BY DEPTNO;
-- DEPTNO 인원수 급여합계
-- ------ ------ --------
-- 10 3 8750 ← 3명 모두 1000 이상이라 그대로 통과
-- 20 4 10075 ← SMITH(800) 제외되어 5명→4명
-- 30 5 8450 ← JAMES(950) 제외되어 6명→5명
--
-- ⭐ 순서가 핵심
-- WHERE 로 '행' 을 먼저 거르고 → GROUP BY 로 묶고 → HAVING 으로 '그룹' 을 거른다.
-- 같은 조건이라도 WHERE 에 쓸지 HAVING 에 쓸지에 따라 결과가 달라질 수 있다.
SQL
복사
판단 기준•
조건이 개별 행에 대한 것 (SAL, DEPTNO, JOB) → WHERE (더 빠름)
•
조건이 그룹 전체에 대한 것 (SUM, COUNT, AVG) → HAVING
7. 다중 컬럼 GROUP BY
1) 여러 기준으로 묶기
-- ============================================================
-- 【이해용 예제】 부서별 · 직무별 인원과 급여합계
-- ============================================================
SELECT DEPTNO AS 부서,
JOB AS 직무,
COUNT(*) AS 인원수,
SUM(SAL) AS 급여합계
FROM EMP
GROUP BY DEPTNO, JOB -- (부서, 직무) 조합마다 하나의 그룹
ORDER BY DEPTNO, JOB;
-- 부서 직무 인원수 급여합계
-- ---- --------- ------ --------
-- 10 CLERK 1 1300
-- 10 MANAGER 1 2450
-- 10 PRESIDENT 1 5000
-- 20 ANALYST 2 6000
-- 20 CLERK 2 1900
-- 20 MANAGER 1 2975
-- 30 CLERK 1 950
-- 30 MANAGER 1 2850
-- 30 SALESMAN 4 5600
-- → 9개 그룹
--
-- ⭐ 07강에서 배운 DISTINCT DEPTNO, JOB 이 9건이었던 것과 같은 원리다.
-- (실제 존재하는 조합의 수만큼 그룹이 생긴다)
SQL
복사
2) NULL도 하나의 그룹이 된다 
-- ============================================================
-- 【시험용 예제】 GROUP BY 는 NULL 을 하나의 그룹으로 묶는다 ★
-- ============================================================
SELECT COMM AS 커미션,
COUNT(*) AS 인원수
FROM EMP
GROUP BY COMM
ORDER BY COMM;
-- 커미션 인원수
-- ------ ------
-- 0 1
-- 300 1
-- 500 1
-- 1400 1
-- (NULL) 10 ← NULL 인 10명이 '하나의 그룹' 으로 묶인다 ⭐
--
-- ⭐ 06강 복습 : NULL 끼리는 비교 불가(UNKNOWN)지만,
-- GROUP BY / DISTINCT 는 예외적으로 NULL 을 '같은 것' 으로 묶는다.
SQL
복사
8. 출제 포인트 한눈에 정리
# | 꼭 외울 것 | 핵심 |
1 | 집계함수 = 다중행 함수 | N행 → 1행 |
2 | 5대 함수 | COUNT SUM AVG MAX MIN |
3 | MAX/MIN | 문자·날짜에도 사용 가능 |
4 | 집계함수와 NULL | SUM AVG MAX MIN COUNT(컬럼) 모두 NULL 무시 |
5 | COUNT(*) | 유일하게 NULL 포함, 전체 행 |
6 | COUNT(컬럼) | 그 컬럼이 NULL 아닌 행 |
7 | COUNT(DISTINCT 컬럼) | 중복·NULL 제거한 값의 종류 |
8 | AVG(COMM) 분모 | NULL 제외 → 값 있는 행 수로 나눔 ![]() |
9 | AVG(COMM) ≠ AVG(NVL(COMM,0)) | 분모가 4 vs 14 로 달라짐 ![]() |
10 | 값 0 은 | NULL이 아니므로 집계에 포함 |
11 | GROUP BY 시 SELECT 규칙 | 그룹 컬럼 or 집계함수만 ![]() |
12 | 위반 시 오류 | ORA-00979: not a GROUP BY expression |
13 | GROUP BY 컬럼 | SELECT에 없어도 됨 (반대는 불가) |
14 | WHERE에 집계함수 | 불가 → HAVING |
15 | WHERE vs HAVING | WHERE=그룹 전(행) / HAVING=그룹 후(그룹) ![]() |
16 | GROUP BY와 NULL | NULL도 하나의 그룹 |
9. 확인 문제
Q1. EMP(14건, COMM은 4명만 값 있음)에서 각 결과는?
표현 | 결과 |
COUNT(*) | ? |
COUNT(COMM) | ? |
정답 보기
Q2. COMM 값이 300, 500, 1400, 0 (나머지 10명은 NULL)일 때 AVG(COMM) 의 결과는?
① 157 ② 550 ③ 2200 ④ 440
정답 보기
Q3. AVG(COMM) 과 AVG(NVL(COMM, 0)) 의 결과가 다른 이유는?
정답 보기
Q4. 다음 SQL이 오류가 나는 이유는?
SELECT DEPTNO, ENAME, SUM(SAL) FROM EMP GROUP BY DEPTNO;
SQL
복사
정답 보기
Q5. "급여합계가 9000을 초과하는 부서"를 찾을 때, 아래 SQL이 오류인 이유와 해결책은?
SELECT DEPTNO, SUM(SAL) FROM EMP WHERE SUM(SAL) > 9000 GROUP BY DEPTNO;
SQL
복사
정답 보기
Q6. WHERE 와 HAVING 의 차이를 실행 순서로 설명하시오.
정답 보기
Q7. SELECT COMM, COUNT(*) FROM EMP GROUP BY COMM 에서 NULL인 커미션은 어떻게 처리되는가?
정답 보기
Q8. MAX(HIREDATE) 와 MIN(HIREDATE) 는 무엇을 의미하는가?
정답 보기
Q9. 아래 두 SQL의 결과가 다를 수 있는 이유는?
-- (A)
SELECT DEPTNO, COUNT(*) FROM EMP GROUP BY DEPTNO;
-- (B)
SELECT DEPTNO, COUNT(*) FROM EMP WHERE SAL >= 1000 GROUP BY DEPTNO;
SQL
복사
정답 보기










쓸 수 없음
쓸 수 있음