[DB] 데이터베이스 개념노트 20편 - 윈도우 함수(Window Function)

데이터베이스 개념노트 20편 - 윈도우 함수(Window Function)

지난 글에서는 여러 SELECT 결과를 합치거나 비교하는 집합 연산자 UNION, UNION ALL, INTERSECT, MINUS에 대해 정리했습니다.

이번 글에서는 SQLD에서 자주 등장하는 심화 SQL 개념인 윈도우 함수(Window Function)에 대해 정리해 보겠습니다.

윈도우 함수는 행들을 특정 기준으로 나누고 정렬한 뒤, 각 행을 유지한 상태에서 순위, 누적합, 이전 값, 다음 값 등을 계산할 수 있는 함수입니다. 처음 보면 어렵게 느껴질 수 있지만, 핵심은 GROUP BY처럼 행을 줄이지 않고, 각 행마다 분석 결과를 붙여주는 함수라고 이해하면 됩니다.


1. 윈도우 함수란?

윈도우 함수(Window Function)는 행과 행 사이의 관계를 이용해 계산하는 함수입니다.

윈도우 함수는 행을 그룹으로 나누고 정렬한 뒤, 각 행마다 순위나 집계 값을 계산하는 SQL 함수이다.

윈도우 함수는 분석 함수라고도 부릅니다. SQLD에서는 순위 함수, 집계 윈도우 함수, 행 순서 함수가 자주 등장합니다.


2. 윈도우 함수가 필요한 이유

일반 집계 함수는 GROUP BY와 함께 사용하면 여러 행을 하나의 결과로 줄입니다. 하지만 실무에서는 각 행은 그대로 보여주면서, 그 행이 속한 그룹의 합계나 순위도 함께 보고 싶은 경우가 많습니다.

예를 들어 주문 목록을 조회하면서 다음 정보를 함께 보고 싶을 수 있습니다.

  • 회원별 주문 순위
  • 회원별 누적 주문금액
  • 전체 주문금액 대비 비율
  • 이전 주문금액과의 차이
  • 카테고리별 매출 순위

이런 경우 윈도우 함수를 사용하면 각 주문 행은 그대로 유지하면서 분석 결과를 함께 출력할 수 있습니다.


3. GROUP BY와 윈도우 함수 차이

구분 GROUP BY 윈도우 함수
결과 행 수 그룹 단위로 행이 줄어듦 기존 행을 유지함
목적 그룹별 집계 결과 조회 각 행에 분석 결과 추가
예시 회원별 총주문금액 각 주문 행에 회원별 총주문금액 표시

GROUP BY는 데이터를 요약하는 데 적합하고, 윈도우 함수는 상세 데이터에 분석 값을 붙이는 데 적합합니다.


4. 예제 테이블

이번 글에서는 다음 주문 테이블을 기준으로 예제를 살펴보겠습니다.

주문번호 회원번호 회원명 카테고리 주문금액 주문일자
O001 M001 김지훈 전자기기 30000 2026-01-10
O002 M001 김지훈 전자기기 15000 2026-01-12
O003 M002 이수진 가구 120000 2026-01-15
O004 M003 박민수 가구 80000 2026-02-01
O005 M002 이수진 전자기기 250000 2026-02-05

5. 윈도우 함수 기본 문법

윈도우 함수는 기본적으로 OVER 절과 함께 사용합니다.

윈도우함수() OVER (
  PARTITION BY 그룹기준컬럼
  ORDER BY 정렬기준컬럼
)
구문 설명
OVER 윈도우 함수가 적용될 범위를 지정한다.
PARTITION BY 행을 그룹으로 나누는 기준을 지정한다.
ORDER BY 각 그룹 안에서 행의 순서를 지정한다.

PARTITION BY는 GROUP BY처럼 그룹을 나누지만, GROUP BY와 달리 결과 행을 줄이지 않습니다.


6. OVER절 이해하기

윈도우 함수에서 가장 중요한 키워드는 OVER입니다. OVER절은 함수가 어떤 범위의 행을 대상으로 계산할지 정합니다.

SUM(주문금액) OVER ()

위 표현은 전체 행을 하나의 범위로 보고 주문금액 합계를 계산합니다.

SUM(주문금액) OVER (PARTITION BY 회원번호)

위 표현은 회원번호별로 범위를 나누고, 각 회원별 주문금액 합계를 계산합니다.


7. PARTITION BY란?

PARTITION BY는 윈도우 함수의 계산 범위를 나누는 기준입니다.

PARTITION BY는 전체 행을 특정 컬럼 기준으로 나누어 윈도우 함수가 적용될 그룹을 만든다.

예를 들어 회원번호별로 주문금액 합계를 구해보겠습니다.

SELECT 주문번호,
       회원번호,
       회원명,
       주문금액,
       SUM(주문금액) OVER (PARTITION BY 회원번호) AS 회원별총주문금액
FROM 주문;

이 SQL은 각 주문 행을 그대로 보여주면서, 같은 회원번호를 가진 주문들의 합계를 함께 출력합니다.


8. GROUP BY와 비교 예시

GROUP BY로 회원별 총주문금액을 구하면 행이 회원 단위로 줄어듭니다.

SELECT 회원번호,
       SUM(주문금액) AS 회원별총주문금액
FROM 주문
GROUP BY 회원번호;

반면 윈도우 함수는 주문 행을 그대로 유지합니다.

SELECT 주문번호,
       회원번호,
       주문금액,
       SUM(주문금액) OVER (PARTITION BY 회원번호) AS 회원별총주문금액
FROM 주문;

즉, GROUP BY는 요약 결과, 윈도우 함수는 상세 행 + 분석 결과라고 이해하면 됩니다.


9. ORDER BY와 윈도우 함수

윈도우 함수 안의 ORDER BY는 각 파티션 안에서 계산 순서를 정합니다.

SELECT 주문번호,
       회원번호,
       주문금액,
       주문일자,
       SUM(주문금액) OVER (
         PARTITION BY 회원번호
         ORDER BY 주문일자
       ) AS 회원별누적주문금액
FROM 주문;

위 SQL은 회원별로 주문일자 순서에 따라 누적 주문금액을 계산합니다.

주의할 점은 윈도우 함수 안의 ORDER BY와 SELECT문 마지막의 ORDER BY는 역할이 다르다는 것입니다.

ORDER BY 위치 역할
OVER 안의 ORDER BY 윈도우 함수 계산 순서 지정
SELECT문 마지막 ORDER BY 최종 조회 결과 출력 순서 지정

10. ROW_NUMBER 함수

ROW_NUMBER는 정렬 기준에 따라 각 행에 고유한 번호를 부여합니다.

ROW_NUMBER는 같은 순위가 있어도 각 행에 서로 다른 번호를 부여하는 순위 함수이다.

SELECT 주문번호,
       회원명,
       주문금액,
       ROW_NUMBER() OVER (ORDER BY 주문금액 DESC) AS 순번
FROM 주문;

주문금액이 높은 순서대로 1번부터 번호를 부여합니다. 동일한 주문금액이 있어도 서로 다른 번호가 부여됩니다.


11. RANK 함수

RANK는 순위를 부여하는 함수입니다. 동일한 값이 있으면 같은 순위를 부여하고, 다음 순위는 건너뜁니다.

SELECT 주문번호,
       회원명,
       주문금액,
       RANK() OVER (ORDER BY 주문금액 DESC) AS 순위
FROM 주문;

예를 들어 1등이 두 명이면 다음 순위는 3등이 됩니다.

주문금액 RANK 결과
250000 1
120000 2
120000 2
80000 4

12. DENSE_RANK 함수

DENSE_RANK는 RANK처럼 같은 값에 같은 순위를 부여하지만, 다음 순위를 건너뛰지 않습니다.

SELECT 주문번호,
       회원명,
       주문금액,
       DENSE_RANK() OVER (ORDER BY 주문금액 DESC) AS 순위
FROM 주문;

예를 들어 1등이 두 명이면 다음 순위는 2등이 됩니다.

주문금액 DENSE_RANK 결과
250000 1
120000 2
120000 2
80000 3

13. ROW_NUMBER, RANK, DENSE_RANK 비교

함수 동점 처리 순위 건너뜀
ROW_NUMBER 동점이어도 서로 다른 번호 부여 해당 없음
RANK 동점이면 같은 순위 건너뜀
DENSE_RANK 동점이면 같은 순위 건너뛰지 않음

SQLD에서는 이 세 함수의 차이를 자주 묻습니다. 특히 RANK는 순위를 건너뛰고, DENSE_RANK는 순위를 건너뛰지 않는다는 점을 기억해야 합니다.


14. PARTITION BY와 순위 함수

PARTITION BY를 사용하면 그룹별 순위를 구할 수 있습니다.

SELECT 주문번호,
       카테고리,
       회원명,
       주문금액,
       RANK() OVER (
         PARTITION BY 카테고리
         ORDER BY 주문금액 DESC
       ) AS 카테고리별순위
FROM 주문;

위 SQL은 카테고리별로 주문금액 순위를 매깁니다. 전자기기 안에서 순위를 따로 매기고, 가구 안에서도 순위를 따로 매깁니다.


15. 집계 윈도우 함수

SUM, AVG, MAX, MIN, COUNT 같은 집계 함수도 OVER절과 함께 사용하면 윈도우 함수처럼 사용할 수 있습니다.

SELECT 주문번호,
       회원번호,
       주문금액,
       SUM(주문금액) OVER (PARTITION BY 회원번호) AS 회원별합계,
       AVG(주문금액) OVER (PARTITION BY 회원번호) AS 회원별평균
FROM 주문;

이 SQL은 각 주문 행을 유지하면서 회원별 합계와 평균을 함께 보여줍니다.


16. 전체 합계와 비율 구하기

OVER절에 PARTITION BY를 쓰지 않으면 전체 행을 하나의 범위로 보고 계산합니다.

SELECT 주문번호,
       회원명,
       주문금액,
       SUM(주문금액) OVER () AS 전체주문금액,
       ROUND(주문금액 / SUM(주문금액) OVER () * 100, 2) AS 주문비율
FROM 주문;

위 SQL은 각 주문금액이 전체 주문금액에서 차지하는 비율을 계산합니다.


17. 누적 합계 구하기

윈도우 함수는 누적 합계를 구할 때도 자주 사용됩니다.

SELECT 주문번호,
       회원번호,
       주문일자,
       주문금액,
       SUM(주문금액) OVER (
         PARTITION BY 회원번호
         ORDER BY 주문일자
         ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
       ) AS 회원별누적합계
FROM 주문;

위 SQL은 회원별로 주문일자 순서에 따라 누적 주문금액을 계산합니다.


18. 윈도우 프레임이란?

윈도우 프레임은 현재 행을 기준으로 윈도우 함수가 계산할 행의 범위를 정하는 구문입니다.

ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW

이 표현은 파티션의 첫 행부터 현재 행까지를 계산 범위로 삼겠다는 의미입니다.

표현 의미
UNBOUNDED PRECEDING 파티션의 첫 행
CURRENT ROW 현재 행
UNBOUNDED FOLLOWING 파티션의 마지막 행

SQLD에서는 윈도우 프레임을 깊게 계산하기보다, 누적합처럼 계산 범위를 조절할 때 사용하는 구문이라고 이해하면 좋습니다.


19. LAG 함수

LAG는 현재 행보다 이전 행의 값을 가져오는 함수입니다.

SELECT 주문번호,
       회원번호,
       주문일자,
       주문금액,
       LAG(주문금액) OVER (
         PARTITION BY 회원번호
         ORDER BY 주문일자
       ) AS 이전주문금액
FROM 주문;

위 SQL은 같은 회원의 이전 주문금액을 현재 행에 함께 표시합니다.

이전 주문과 현재 주문의 차이를 계산할 수도 있습니다.

SELECT 주문번호,
       회원번호,
       주문금액,
       주문금액 - LAG(주문금액) OVER (
         PARTITION BY 회원번호
         ORDER BY 주문일자
       ) AS 이전주문과차이
FROM 주문;

20. LEAD 함수

LEAD는 현재 행보다 다음 행의 값을 가져오는 함수입니다.

SELECT 주문번호,
       회원번호,
       주문일자,
       주문금액,
       LEAD(주문금액) OVER (
         PARTITION BY 회원번호
         ORDER BY 주문일자
       ) AS 다음주문금액
FROM 주문;

LAG는 이전 행, LEAD는 다음 행을 가져온다고 기억하면 됩니다.

함수 역할
LAG 이전 행의 값을 가져온다.
LEAD 다음 행의 값을 가져온다.

21. FIRST_VALUE 함수

FIRST_VALUE는 윈도우 범위에서 첫 번째 값을 가져오는 함수입니다.

SELECT 주문번호,
       카테고리,
       주문금액,
       FIRST_VALUE(주문금액) OVER (
         PARTITION BY 카테고리
         ORDER BY 주문금액 DESC
       ) AS 카테고리최고주문금액
FROM 주문;

위 SQL은 카테고리별로 주문금액이 가장 큰 값을 각 행에 표시합니다.


22. LAST_VALUE 함수

LAST_VALUE는 윈도우 범위에서 마지막 값을 가져오는 함수입니다.

SELECT 주문번호,
       카테고리,
       주문금액,
       LAST_VALUE(주문금액) OVER (
         PARTITION BY 카테고리
         ORDER BY 주문금액 DESC
         ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
       ) AS 카테고리최저주문금액
FROM 주문;

LAST_VALUE는 윈도우 프레임 범위에 영향을 받을 수 있으므로 주의해야 합니다. 전체 파티션의 마지막 값을 보고 싶다면 프레임 범위를 명확히 지정하는 것이 좋습니다.


23. NTILE 함수

NTILE은 정렬된 결과를 지정한 개수의 그룹으로 나누는 함수입니다.

SELECT 주문번호,
       회원명,
       주문금액,
       NTILE(4) OVER (ORDER BY 주문금액 DESC) AS 분위
FROM 주문;

위 SQL은 주문금액이 높은 순서대로 데이터를 4개 그룹으로 나눕니다. 매출 상위 그룹, 중간 그룹, 하위 그룹을 나눌 때 활용할 수 있습니다.


24. RATIO_TO_REPORT 함수

Oracle에서는 RATIO_TO_REPORT 함수를 사용해 전체 합계 대비 비율을 구할 수 있습니다.

SELECT 주문번호,
       회원명,
       주문금액,
       RATIO_TO_REPORT(주문금액) OVER () AS 전체대비비율
FROM 주문;

이 함수는 각 행의 값이 전체 합계에서 차지하는 비율을 반환합니다.

PARTITION BY를 함께 사용하면 그룹 내 비율을 구할 수 있습니다.

SELECT 주문번호,
       카테고리,
       주문금액,
       RATIO_TO_REPORT(주문금액) OVER (PARTITION BY 카테고리) AS 카테고리내비율
FROM 주문;

25. 윈도우 함수 사용 위치

윈도우 함수는 주로 SELECT절과 ORDER BY절에서 사용할 수 있습니다.

SELECT 주문번호,
       주문금액,
       RANK() OVER (ORDER BY 주문금액 DESC) AS 순위
FROM 주문
ORDER BY 순위;

윈도우 함수는 WHERE절에서 직접 사용할 수 없습니다. 윈도우 함수 결과를 조건으로 필터링하고 싶다면 인라인 뷰를 사용할 수 있습니다.

SELECT *
FROM (
  SELECT 주문번호,
         주문금액,
         RANK() OVER (ORDER BY 주문금액 DESC) AS 순위
  FROM 주문
)
WHERE 순위 <= 3;

위 SQL은 주문금액 상위 3개 주문을 조회하는 예시입니다.


26. 윈도우 함수 실행 순서 이해

SQL의 논리적 실행 순서를 생각하면 윈도우 함수는 WHERE, GROUP BY, HAVING 이후 SELECT 단계에서 계산됩니다.

순서 구문 역할
1 FROM 조회 대상 테이블 결정
2 WHERE 행 필터링
3 GROUP BY 그룹화
4 HAVING 그룹 필터링
5 SELECT 윈도우 함수 계산
6 ORDER BY 최종 결과 정렬

그래서 윈도우 함수의 결과를 WHERE절에서 바로 사용할 수 없습니다. 필터링이 필요하면 인라인 뷰로 감싸서 바깥쪽 쿼리에서 조건을 적용합니다.


27. 윈도우 함수에서 자주 하는 실수

실수 주의할 점
GROUP BY처럼 행이 줄어든다고 생각함 윈도우 함수는 기존 행을 유지한다.
OVER절을 빼먹음 윈도우 함수는 OVER절과 함께 사용한다.
RANK와 DENSE_RANK 차이를 헷갈림 RANK는 순위를 건너뛰고, DENSE_RANK는 건너뛰지 않는다.
ROW_NUMBER와 RANK를 같다고 생각함 ROW_NUMBER는 동점이어도 고유 번호를 부여한다.
윈도우 함수 결과를 WHERE절에서 바로 사용함 인라인 뷰로 감싼 뒤 바깥 쿼리에서 필터링한다.
OVER 안의 ORDER BY와 최종 ORDER BY를 혼동함 하나는 계산 순서, 하나는 출력 순서이다.

28. SQLD 관점에서 꼭 기억할 내용

이번 글에서 SQLD 공부를 위해 꼭 기억해야 할 내용은 다음과 같습니다.

  • 윈도우 함수는 행을 유지한 채 분석 값을 계산한다.
  • 윈도우 함수는 OVER절과 함께 사용한다.
  • PARTITION BY는 계산 범위를 그룹으로 나눈다.
  • ORDER BY는 파티션 안에서 계산 순서를 정한다.
  • ROW_NUMBER는 각 행에 고유 번호를 부여한다.
  • RANK는 동점에 같은 순위를 부여하고 다음 순위를 건너뛴다.
  • DENSE_RANK는 동점에 같은 순위를 부여하지만 다음 순위를 건너뛰지 않는다.
  • SUM, AVG, MAX, MIN도 OVER절과 함께 사용하면 윈도우 함수처럼 동작한다.
  • LAG는 이전 행, LEAD는 다음 행의 값을 가져온다.
  • 윈도우 함수 결과를 WHERE절에서 바로 사용할 수 없다.
  • 필터링이 필요하면 인라인 뷰를 활용한다.

29. 핵심 요약

개념 핵심 내용
윈도우 함수 행을 유지한 상태에서 순위, 집계, 이전/다음 값 등을 계산하는 함수
OVER 윈도우 함수의 계산 범위를 지정하는 절
PARTITION BY 계산 대상을 그룹으로 나누는 기준
ORDER BY 파티션 안에서 계산 순서를 지정
ROW_NUMBER 각 행에 고유한 번호 부여
RANK 동점은 같은 순위, 다음 순위는 건너뜀
DENSE_RANK 동점은 같은 순위, 다음 순위는 건너뛰지 않음
LAG 이전 행의 값을 가져옴
LEAD 다음 행의 값을 가져옴
NTILE 정렬된 행을 지정한 개수의 그룹으로 나눔

30. 마무리

이번 글에서는 SQLD 심화 SQL에서 자주 등장하는 윈도우 함수에 대해 정리했습니다.

윈도우 함수는 GROUP BY처럼 데이터를 요약하기만 하는 것이 아니라, 기존 행을 유지한 상태에서 순위, 누적합, 그룹별 합계, 이전 값, 다음 값 같은 분석 결과를 함께 보여줍니다.

SQLD에서는 ROW_NUMBER, RANK, DENSE_RANK의 차이와 PARTITION BY, ORDER BY, OVER절의 역할을 정확히 이해하는 것이 중요합니다. 또한 윈도우 함수 결과를 WHERE절에서 바로 사용할 수 없고, 필요하면 인라인 뷰로 감싸서 필터링해야 한다는 점도 기억해야 합니다.