•
윈도우 함수가 GROUP BY 와 달리 행을 보존한다는 것을 안다.
•
OVER 절의 PARTITION BY · ORDER BY · WINDOWING 구조를 이해한다.
•
RANK · DENSE_RANK · ROW_NUMBER 의 차이를 설명할 수 있다.
•
WINDOWING 절로 누적합계·이동평균을 만들 수 있다.
•
LAG · LEAD 로 이전/다음 행의 값을 가져올 수 있다.
1. 윈도우 함수란
1) GROUP BY 와 정반대 — 행을 보존한다 
GROUP BY 집계 (11강) | 윈도우 함수 | |
행 수 | 여러 행 → 1행으로 압축 | 행을 그대로 유지 |
개별 값 | 사라짐 | 보존 |
예 | 부서별 평균 1건 | 각 사원 + 그 부서 평균 |
-- ============================================================
-- 【이해용 예제】 각 사원의 급여 옆에 '그 부서의 평균' 을 함께 ★
-- ============================================================
-- GROUP BY : 부서별 1행으로 압축 (개별 사원이 사라짐)
SELECT DEPTNO, AVG(SAL) FROM EMP GROUP BY DEPTNO; -- 3행
-- 윈도우 함수 : 사원 14명을 그대로 두고 부서 평균을 옆에 붙임
SELECT ENAME, DEPTNO, SAL,
AVG(SAL) OVER (PARTITION BY DEPTNO) AS 부서평균 -- 행 보존!
FROM EMP;
-- ENAME DEPTNO SAL 부서평균
-- ------ ------ ----- --------
-- CLARK 10 2450 2917 ← 10번 부서 평균
-- KING 10 5000 2917 ← 같은 값이 각 행에 반복
-- MILLER 10 1300 2917
-- SMITH 20 800 2175
-- ... → 14행 그대로 (개별 급여 + 부서평균)
--
-- ⭐ 이게 윈도우 함수의 핵심: "집계는 하되 행은 남긴다"
SQL
복사
2. OVER 절 구조 

1) 윈도우 함수의 기본 문법
윈도우함수( 인자 ) OVER ( [PARTITION BY 컬럼] -- 그룹 나누기
[ORDER BY 컬럼] -- 정렬
[WINDOWING 절] ) -- 행 범위 지정
SQL
복사
요소 | 역할 | 생략하면 |
PARTITION BY | 전체를 소그룹으로 나눔 | 전체가 하나의 그룹 |
ORDER BY | 파티션 내 정렬 | 순위 함수는 오류 |
WINDOWING | 계산 대상 행 범위 | 기본 범위 적용 |
OVER () 를 빈 괄호로 쓰면 전체를 하나의 윈도우로 봅니다.
SUM(SAL) OVER () = 전체 급여 합계를 모든 행에 표시.3. 순위 함수 


1) 세 함수의 차이 — 이 표가 핵심
-- ============================================================
-- 【시험용 예제】 RANK / DENSE_RANK / ROW_NUMBER 를 나란히 ★★★
-- ============================================================
SELECT ENAME, SAL,
RANK() OVER (ORDER BY SAL DESC) AS RANK순위,
DENSE_RANK() OVER (ORDER BY SAL DESC) AS DENSE순위,
ROW_NUMBER() OVER (ORDER BY SAL DESC) AS ROW번호
FROM EMP;
-- ENAME SAL RANK순위 DENSE순위 ROW번호
-- ------ ----- -------- --------- -------
-- KING 5000 1 1 1
-- SCOTT 3000 2 2 2 ┐ 급여 3000 공동
-- FORD 3000 2 2 3 ┘
-- JONES 2975 4 ⚠️ 3 4 ← 여기가 핵심!
-- BLAKE 2850 5 4 5
-- CLARK 2450 6 5 6
-- ...
SQL
복사
함수 | 공동 순위 처리 | 예시 |
RANK | 공동 순위 후 건너뜀 | 1, 2, 2, 4, 5 |
DENSE_RANK | 공동 순위 후 안 건너뜀 | 1, 2, 2, 3, 4 |
ROW_NUMBER | 무조건 고유 번호 | 1, 2, 3, 4, 5 |
핵심 구분•
3000 이 둘(SCOTT, FORD) → 셋 다 2위로 처리
•
다음 2975(JONES): RANK는 4위(2,3을 건너뜀), DENSE_RANK는 3위(안 건너뜀), ROW_NUMBER는 4번(순위와 무관한 고유번호)
2) PARTITION BY 로 부서별 순위
-- ============================================================
-- 【이해용 예제】 부서별 급여 순위 ★
-- ============================================================
SELECT ENAME, DEPTNO, SAL,
RANK() OVER (PARTITION BY DEPTNO -- 부서별로 나눠서
ORDER BY SAL DESC) AS 부서내순위 -- 각 부서 안에서 순위
FROM EMP;
-- ENAME DEPTNO SAL 부서내순위
-- ------ ------ ----- ----------
-- KING 10 5000 1 ┐
-- CLARK 10 2450 2 │ 10번 부서 안 순위
-- MILLER 10 1300 3 ┘
-- SCOTT 20 3000 1 ┐
-- FORD 20 3000 1 │ 20번 부서 안 순위 (공동 1위)
-- JONES 20 2975 3 │
-- ... ← 부서가 바뀌면 순위가 1부터 다시
--
-- ⭐ PARTITION BY 로 나눈 각 부서에서 순위가 독립적으로 매겨진다.
SQL
복사
3)
순위 함수는 ORDER BY 필수
SELECT ENAME, RANK() OVER () FROM EMP; -- ORDER BY 없음
-- ORA-30485: missing ORDER BY expression in the window specification
-- → 순위는 '무엇을 기준으로' 매길지 정해야 하므로 ORDER BY 가 반드시 필요하다
SQL
복사
4. 집계 윈도우 함수 

1) SUM/AVG/COUNT/MAX/MIN OVER
-- ============================================================
-- 【이해용 예제】 부서별 집계를 각 행에 붙이기 ★
-- ============================================================
SELECT ENAME, DEPTNO, SAL,
SUM(SAL) OVER (PARTITION BY DEPTNO) AS 부서합계,
MAX(SAL) OVER (PARTITION BY DEPTNO) AS 부서최고,
COUNT(*) OVER (PARTITION BY DEPTNO) AS 부서인원
FROM EMP;
-- ENAME DEPTNO SAL 부서합계 부서최고 부서인원
-- ------ ------ ----- -------- -------- --------
-- CLARK 10 2450 8750 5000 3
-- KING 10 5000 8750 5000 3
-- MILLER 10 1300 8750 5000 3
-- SMITH 20 800 10875 3000 5
-- ...
--
-- ⭐ 11강 GROUP BY 로 구하던 부서 합계를, 개별 사원 행을 유지한 채 옆에 붙였다.
-- "내 급여가 부서 합계에서 차지하는 비중" 같은 계산을 쉽게 할 수 있다.
SQL
복사
5. WINDOWING 절 — 계산 대상 행 범위 


가장 어려운 부분입니다. ORDER BY 와 함께 쓰면 "어디부터 어디까지" 계산할지 지정합니다.1) ROWS vs RANGE
ROWS | RANGE | |
기준 | 물리적 행 개수 | 논리적 값 범위 |
예 | "앞 2행" | "값이 100 이내인 행" |
2) 범위 지정 키워드
키워드 | 의미 |
UNBOUNDED PRECEDING | 파티션의 첫 행 |
n PRECEDING | 현재 행에서 n행 앞 |
CURRENT ROW | 현재 행 |
n FOLLOWING | 현재 행에서 n행 뒤 |
UNBOUNDED FOLLOWING | 파티션의 마지막 행 |
3) 누적합계 — 첫 행부터 현재 행까지 
-- ============================================================
-- 【시험용 예제】 누적합계 (급여 낮은 순으로 쌓기) ★★
-- ============================================================
SELECT ENAME, SAL,
SUM(SAL) OVER (ORDER BY SAL
ROWS BETWEEN UNBOUNDED PRECEDING -- 첫 행부터
AND CURRENT ROW) AS 누적합계 -- 현재 행까지
FROM EMP;
-- ENAME SAL 누적합계
-- ------ ----- --------
-- SMITH 800 800 ← 800
-- JAMES 950 1750 ← 800+950
-- ADAMS 1100 2850 ← 800+950+1100
-- WARD 1250 4100 ← 누적...
-- ...
-- KING 5000 29025 ← 마지막 = 전체 합계
--
-- ⭐ ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW = 누적합계 공식
SQL
복사
4) 세 가지 대표 윈도우 
-- ============================================================
-- 【시험용 예제】 윈도우 범위별 결과 비교 ★★
-- ============================================================
SELECT ENAME, SAL,
-- ① 누적합계 : 첫 행 ~ 현재
SUM(SAL) OVER (ORDER BY SAL
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS 누적,
-- ② 이동합계 : 앞1행 ~ 뒤1행 (3개 행)
SUM(SAL) OVER (ORDER BY SAL
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS 이동3행,
-- ③ 전체합계 : 첫 행 ~ 마지막 행
SUM(SAL) OVER (ORDER BY SAL
ROWS BETWEEN UNBOUNDED PRECEDING
AND UNBOUNDED FOLLOWING) AS 전체
FROM EMP;
SQL
복사
윈도우 범위 | 의미 |
UNBOUNDED PRECEDING ~ CURRENT ROW | 누적합계 |
1 PRECEDING ~ 1 FOLLOWING | 이동평균/합계 (앞뒤 1행) |
UNBOUNDED PRECEDING ~ UNBOUNDED FOLLOWING | 전체합계 (= OVER()) |
ORDER BY 만 쓰고 WINDOWING 을 생략하면 기본값은
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW (누적) 입니다.6. 행 순서 함수 

1) LAG / LEAD — 이전/다음 행 값 가져오기
-- ============================================================
-- 【시험용 예제】 앞뒤 사원의 급여를 함께 보기 ★★
-- ============================================================
SELECT ENAME, SAL,
LAG(SAL) OVER (ORDER BY SAL) AS 이전급여, -- 바로 앞 행의 SAL
LEAD(SAL) OVER (ORDER BY SAL) AS 다음급여, -- 바로 다음 행의 SAL
SAL - LAG(SAL) OVER (ORDER BY SAL) AS 차이 -- 앞 사람과의 차이
FROM EMP;
-- ENAME SAL 이전급여 다음급여 차이
-- ------ ----- -------- -------- ------
-- SMITH 800 (NULL) 950 (NULL) ← 첫 행은 이전이 없어 NULL
-- JAMES 950 800 1100 150
-- ADAMS 1100 950 1250 150
-- ...
-- KING 5000 3000 (NULL) 2000 ← 마지막 행은 다음이 없어 NULL
--
-- ⭐ LAG(SAL, n, 기본값) : n행 앞 (기본 1), 없으면 기본값
-- LAG(SAL, 2, 0) → 2행 앞, 없으면 0
SQL
복사
함수 | 가져오는 값 | 기본 offset |
LAG(컬럼) | 이전 행 값 | 1 |
LEAD(컬럼) | 다음 행 값 | 1 |
FIRST_VALUE(컬럼) | 윈도우 첫 값 | — |
LAST_VALUE(컬럼) | 윈도우 마지막 값 | — |
7. 비율·그룹 함수 
함수 | 기능 | 반환 |
NTILE(n) | 데이터를 n개 그룹으로 나눔 | 그룹 번호 |
RATIO_TO_REPORT | 전체 합계 대비 비율 | 0~1 |
PERCENT_RANK | 백분율 순위 | 0~1 |
CUME_DIST | 누적 분포 | 0~1 |
-- ============================================================
-- 【이해용 예제】 NTILE 로 급여 상위/하위 그룹 나누기 ★
-- ============================================================
SELECT ENAME, SAL,
NTILE(4) OVER (ORDER BY SAL DESC) AS 급여4등급, -- 4개 그룹으로
ROUND(RATIO_TO_REPORT(SAL) OVER (), 3) AS 급여비중 -- 전체 대비 비율
FROM EMP;
-- ENAME SAL 급여4등급 급여비중
-- ------ ----- --------- --------
-- KING 5000 1 0.172 ← 상위 1등급, 전체의 17.2%
-- SCOTT 3000 1 0.103
-- ...
-- SMITH 800 4 0.028 ← 하위 4등급
--
-- ⭐ NTILE(4) = 4분위. 상위 25%, 25~50%, ... 로 나눈다.
SQL
복사
8. 윈도우 함수의 위치 제약
1) 윈도우 함수는 WHERE 에 못 쓴다
-- ❌ 윈도우 함수를 WHERE 에 직접 쓸 수 없다
SELECT ENAME FROM EMP
WHERE RANK() OVER (ORDER BY SAL DESC) = 1; -- 오류!
-- → 윈도우 함수는 SELECT 단계에서 계산되므로 WHERE(먼저 실행)에서 못 쓴다 (07강)
-- ✅ 인라인 뷰로 감싸서 바깥에서 조건을 건다
SELECT * FROM (
SELECT ENAME, SAL,
RANK() OVER (ORDER BY SAL DESC) AS RK
FROM EMP
) WHERE RK = 1;
-- → 급여 1위 (KING)
SQL
복사
2) "부서별 최고급여자"
-- ============================================================
-- 【시험용 예제】
-- ============================================================
SELECT ENAME, SAL, DEPTNO FROM (
SELECT ENAME, SAL, DEPTNO,
RANK() OVER (PARTITION BY DEPTNO ORDER BY SAL DESC) AS RK
FROM EMP
) WHERE RK = 1;
-- ENAME SAL DEPTNO
-- ------ ----- ------
-- KING 5000 10
-- SCOTT 3000 20
-- FORD 3000 20 ← 공동 1위도 자연히 포함
-- BLAKE 2850 30
SQL
복사
방법 | 평가 |
다중행 IN | 다른 부서와 겹치면 부정확 ![]() |
상관 서브쿼리 | 정확하나 행마다 반복 |
윈도우 RANK | 정확 + 간결 + 공동순위 자동 ![]() |
9. 출제 포인트 한눈에 정리
# | 꼭 외울 것 | 핵심 |
1 | 윈도우 함수 특징 | 행을 보존 (GROUP BY와 반대) ![]() |
2 | 기본 구조 | 함수() OVER (PARTITION BY ~ ORDER BY ~ WINDOWING) |
3 | PARTITION BY | 윈도우판 GROUP BY (행 유지) |
4 | RANK | 공동순위 후 건너뜀 (1,2,2,4) ![]() |
5 | DENSE_RANK | 공동순위 후 안 건너뜀 (1,2,2,3) ![]() |
6 | ROW_NUMBER | 무조건 고유 (1,2,3,4) ![]() |
7 | 순위 함수 | ORDER BY 필수 |
8 | 집계 윈도우 | SUM/AVG OVER — 행 유지 집계 |
9 | ROWS vs RANGE | 물리적 행 vs 논리적 값 ![]() |
10 | 누적합계 | ROWS UNBOUNDED PRECEDING AND CURRENT ROW |
11 | 전체합계 | UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING = OVER() |
12 | LAG/LEAD | 이전/다음 행 (기본 offset 1) ![]() |
13 | FIRST/LAST_VALUE | 윈도우 첫/마지막 값 |
14 | NTILE(n) | n개 그룹으로 분할 |
15 | RATIO_TO_REPORT | 전체 대비 비율 |
16 | 윈도우 함수 위치 | WHERE 불가 → 인라인뷰로 감쌈 ![]() |
10. 확인 문제
Q1. 급여가 5000, 3000, 3000, 2975 일 때 RANK 와 DENSE_RANK 의 순위는?
정답 보기
Q2. ROW_NUMBER 가 RANK 와 다른 점은?
정답 보기
Q3. 순위 함수에 ORDER BY 가 반드시 필요한 이유는?
정답 보기
Q4. 누적합계를 구하는 WINDOWING 절은?
정답 보기
Q5. ROWS 와 RANGE 의 차이는?
정답 보기
Q6. LAG(SAL, 2, 0) OVER (ORDER BY SAL) 은 무엇을 반환하는가?
정답 보기
Q7. NTILE(4) 는 무엇을 하는가?
정답 보기







