SQL

SQL: 윈도우 함수 문법 구조

dewstream 2025. 11. 24. 08:00
728x90

※ SQL: Window Function Syntax Structure.

 

안녕하세요. 듀스트림입니다.

 

또다시 월요일입니다.

 

오늘 포스팅은 SQL을 다룬다면, 필수적으로 이해하고 있어야 하는 윈도우 함수의 문법 구조에 대해 알아보겠습니다.


1. 윈도우 함수의 기본 문법 구조

Window-function ( [expression] ) OVER (
      [PARTITION BY ...]
      [ORDER BY ...]
      [ROWS | RANGE ...]
)

 

한 줄로 표현하면 아래와 같은 구조로 되어있습니다.

윈도우 함수 = 함수 + OVER(윈도우 정의)
  • 윈도우 함수 자체: SUM(), AVG(), ROW_NUMBER(), LAG(), LEAD() 등
  • OVER() 안의 윈도우 정의: 어떤 범위(window)에서 계산할지를 지정

 

ANSI 표준 윈도우 함수 목록

 

  • 순위 함수: ROW_NUMBER(), RANK(), DENSE_RANK()
  • 이동 함수: LAG(), LEAD()
  • 누적/집계: SUM(), COUNT(), AVG(), MIN(), MAX() (윈도우 버전으로 사용 가능)

 


2. 구성 요소 설명

2.1 PARTITION BY

  • 그룹을 나누는 역할
PARTITION BY user_id

2.2 ORDER BY

  • 윈도우 내부에서 정렬 기준
ORDER BY created_at

 


2.3 ROWS / RANGE

  • 정렬된 데이터에서 어디까지를 윈도우 범위로 볼 것인지 지정 (윈도우 프레임 지정 방식)
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW

+ expression

Window-function ( [expression] )

표현식 자리는 일반적인 스칼라 표현식이면 가능하다고 생각하시면 됩니다.

  • 컬럼
  • 연산식
  • 함수 호출
  • CASE WHEN
  • 타입 변환
  • 산술/문자/날짜 연산

 

❗주의점으로는 다른 윈도우 함수나 서브쿼리는 넣을 수 없습니다.

단, SELECT 절의 서브 쿼리 결과 ALIAS를 윈도우 함수가 참조하는 형태는 가능합니다.

 

예를 들면 아래 쿼리들처럼 사용 가능합니다:

-- 서브쿼리 예시 1: 상관 서브쿼리
SELECT
    t1.*,
    (
        SELECT SUM(amount)
        FROM t2
        WHERE t2.user_id = t1.user_id
    ) AS sub_amt,
    SUM(
        (
            SELECT SUM(amount)
            FROM t2
            WHERE t2.user_id = t1.user_id
        )
    ) OVER (PARTITION BY t1.dept_id ORDER BY t1.created_at) AS win_sum_sub_amt
FROM t1;
-- 서브쿼리 예시 2: 한 번만 계산해서 결과를 재사용하는 방식
SELECT
    t1.*,
    sub_amt,
    SUM(sub_amt) OVER (PARTITION BY t1.dept_id ORDER BY t1.created_at) AS win_sum_sub_amt
FROM (
    SELECT
        t1.*,
        (SELECT SUM(amount) FROM t2 WHERE t2.user_id = t1.user_id) AS sub_amt
    FROM t1
) t1;
-- CTE 형식 1: 한 번만 계산해서 결과를 재사용하는 방식
-- 서브쿼리 예시 2와 동일한 구조인데 CTE로 가독성만 향상시킨 버전입니다.
WITH base AS (
    SELECT
        t1.*,
        /** 1.스칼라 서브쿼리로 사용자별 합계를 미리 계산 **/
        (
            SELECT SUM(amount)
            FROM t2
            WHERE t2.user_id = t1.user_id
        ) AS sub_amt
    FROM t1
)
SELECT
    base.*,
    /** 2. CTE에서 만든 sub_amt를 가지고 윈도우 함수 수행 **/
    SUM(sub_amt) OVER (
        PARTITION BY dept_id
        ORDER BY created_at
    ) AS win_sum_sub_amt
FROM base;
-- CTE 형식 2: JOIN + GROUP BY
/**
* (SELECT SUM(amount) FROM t2 WHERE t2.user_id = t1.user_id)를
* 미리 사용자별로 한 번에 계산해서 테이블(agg_t2)로 만들고
* 그 테이블을 t1에 join하는 구조로 데이터량이 많을수록 일반적으로 성능에서 유리한 구조입니다.
**/
WITH agg_t2 AS (
    SELECT
        user_id,
        SUM(amount) AS sub_amt
    FROM t2
    GROUP BY user_id
),
base AS (
    SELECT
        t1.*,
        a.sub_amt
    FROM t1
    LEFT JOIN agg_t2 a
        ON a.user_id = t1.user_id
)
SELECT
    base.*,
    SUM(sub_amt) OVER (
        PARTITION BY dept_id
        ORDER BY created_at
    ) AS win_sum_sub_amt
FROM base;

3. 예제

▸ 예제1: 누적 합계

SELECT
    account_id,
    amount,
    SUM(amount) OVER(
        PARTITION BY account_id
        ORDER BY created_at
    ) AS running_total
FROM payments;

▸ 예제2: 최근 행 참조 (LEAD)

SELECT
    log_seq,
    created_at,
    LEAD(log_seq) OVER(ORDER BY created_at) AS next_log
FROM log_index;

▸ 예제3: 순위 매기기

SELECT
    user_id,
    score,
    RANK() OVER(ORDER BY score DESC) AS rank
FROM game_score;

요약하자면,

  • 윈도우 함수는 일반 함수가 아니라 "행 집합 위에서 계산"하는 함수
  • OVER() 는 필수
  • OVER 안 옵션은 선택(PARTITION / ORDER BY / ROWS 등)
  • GROUP BY 와 다르게 행을 줄이지 않는다.

예제를 이렇게 많이 작성할 생각은 없었는데..

다 작성하고 나면 너무 짧을 때도 있고 예상과 다르게 내용이 너무 길어질 때도 있는 거 같아요.

 

오늘은 여기까지~

 

 

728x90

'SQL' 카테고리의 다른 글

SQL: 행 스트림  (0) 2025.11.17
SQL: 상관관계(상관 서브쿼리)  (0) 2025.10.27
SQL: 실행 순서  (0) 2025.10.24
SQL: EXISTS / NOT EXISTS  (0) 2025.10.15
SQL: ASC, DESC  (0) 2025.10.13