Search
moon
sun

20.윈도우 함수

•
윈도우 함수가 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) 는 무엇을 하는가?
정답 보기