[SQL] 윈도우 함수(Window Function)

khj·2026년 1월 5일

SQL

목록 보기
4/6
post-thumbnail

MSSQL에서 윈도우 함수(Window Function)
그룹핑 없이도 행 단위 결과를 유지하면서 집계, 순위, 누적 계산을 할 수 있는 기능이다.

기존 GROUP BY로는 해결하기 어려운 쿼리를
훨씬 직관적이고 성능 좋게 작성할 수 있다.


윈도우 함수 기본 구조

함수명 (컬럼)
OVER (
  PARTITION BY 컬럼
  ORDER BY 컬럼
  ROWS | RANGE
)

구성 요소 설명

  • PARTITION BY : 그룹 기준
  • ORDER BY : 윈도우 내 정렬 기준
  • ROWS / RANGE : 계산 범위 지정

예제 테이블

Orders

order_idmember_idpriceorder_date
11100002024-01-01
21200002024-01-05
32150002024-01-03
42300002024-01-10

1. ROW_NUMBER()

개념

  • 중복 없는 순번
  • 페이징, 최신 데이터 조회에 자주 사용
SELECT *,
       ROW_NUMBER() OVER (
         PARTITION BY member_id
         ORDER BY order_date DESC
       ) AS rn
FROM Orders;

회원별 최신 주문 조회

SELECT *
FROM (
  SELECT *,
         ROW_NUMBER() OVER (
           PARTITION BY member_id
           ORDER BY order_date DESC
         ) AS rn
  FROM Orders
) t
WHERE rn = 1;

2. RANK() / DENSE_RANK()

  • RANK() : 동일 순위 발생 시 다음 순위 건너뜀
  • DENSE_RANK() : 동일 순위 발생 시 다음 순위 연속
SELECT member_id,
       price,
       RANK() OVER (ORDER BY price DESC) AS rank,
       DENSE_RANK() OVER (ORDER BY price DESC) AS dense_rank
FROM Orders;

3. SUM() OVER (누적 합계)

누적 매출 계산

SELECT order_id,
       order_date,
       price,
       SUM(price) OVER (
         ORDER BY order_date
         ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
       ) AS cumulative_price
FROM Orders;

4. AVG() OVER (이동 평균)

최근 3건 평균

SELECT order_id,
       order_date,
       price,
       AVG(price) OVER (
         ORDER BY order_date
         ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
       ) AS avg_price
FROM Orders;

5. COUNT() OVER

회원별 주문 수

SELECT order_id,
       member_id,
       COUNT(*) OVER (PARTITION BY member_id) AS order_count
FROM Orders;

6. LAG() / LEAD()

이전 / 다음 행 값 조회

SELECT order_id,
       order_date,
       price,
       LAG(price) OVER (ORDER BY order_date) AS prev_price,
       LEAD(price) OVER (ORDER BY order_date) AS next_price
FROM Orders;

전일 대비 증감

SELECT order_id,
       price,
       price - LAG(price) OVER (ORDER BY order_date) AS diff
FROM Orders;

7. FIRST_VALUE() / LAST_VALUE()

LAST_VALUE는 범위 지정 필수

SELECT member_id,
       order_date,
       price,
       FIRST_VALUE(price) OVER (
         PARTITION BY member_id
         ORDER BY order_date
       ) AS first_price,
       LAST_VALUE(price) OVER (
         PARTITION BY member_id
         ORDER BY order_date
         ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
       ) AS last_price
FROM Orders;

8. PARTITION BY 활용 예제

회원별 누적 주문 금액

SELECT member_id,
       order_date,
       price,
       SUM(price) OVER (
         PARTITION BY member_id
         ORDER BY order_date
         ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
       ) AS member_total
FROM Orders;

9. 윈도우 함수 vs GROUP BY

GROUP BY

SELECT member_id, SUM(price)
FROM Orders
GROUP BY member_id;
  • 행 단위 데이터 손실

Window Function

SELECT order_id,
       member_id,
       price,
       SUM(price) OVER (PARTITION BY member_id) AS total_price
FROM Orders;
  • 행 유지 + 집계 동시 처리

실무 사용 시 주의사항

  • ORDER BY 없는 OVER는 전체 파티션 기준
  • 윈도우 함수는 WHERE 절에서 직접 사용 불가
  • 서브쿼리 또는 CTE로 감싸서 사용
WITH Ranked AS (
  SELECT *,
         ROW_NUMBER() OVER (ORDER BY order_date DESC) AS rn
  FROM Orders
)
SELECT *
FROM Ranked
WHERE rn <= 10;

정리

  • 윈도우 함수는 행을 유지하면서 집계 가능
  • GROUP BY의 대체가 아닌 보완 관계
  • 페이징, 랭킹, 누적 계산에 매우 강력
profile
Spring, Django 개발 블로그

0개의 댓글