본문 바로가기
WIKI 기술 지식 베이스

윈도우 함수

원문 보기 위키 갱신

윈도우 함수 (Window Functions)

윈도우 함수는 현재 행과 관련된 행 집합 전체에 걸쳐 계산을 수행하면서도 행을 하나로 뭉치지 않아요. 입력 행마다 대응하는 출력 행이 나오고, 그 옆에 윈도우 함수 결과가 붙어요.

출처: 문서

본문

윈도우 함수는 현재 행과 관련된 행 집합 전체에 걸쳐 계산을 수행해요. GROUP BY를 쓰는 집계 함수와 달리, 윈도우 함수는 행들을 하나의 출력 행으로 뭉치지 않아요. 모든 입력 행은 대응하는 출력 행을 만들고, 그 결과에 윈도우 함수 결과가 덧붙여져요.

Turso는 기본 프레임 정의를 쓰는 집계 함수의 윈도우 함수 사용, `row_number()` 랭킹 함수, `FILTER (WHERE ...)` 절을 지원해요. 나머지 전용 윈도우 함수(`rank`, `dense_rank`, `ntile`, `lag`, `lead`, `first_value`, `last_value`, `nth_value`)와 커스텀 프레임 지정(명시적 경계를 갖는 ROWS, RANGE, GROUPS)은 아직 지원되지 않아요.

문법

aggregate_function(expression) OVER (
    [PARTITION BY expression [, ...]]
    [ORDER BY expression [ASC | DESC] [, ...]]
)
절 설명
aggregate_function 지원되는 아무 집계 함수: count, sum, avg, min, max, total, group_concat
OVER (...) 함수가 동작할 윈도우를 정의해요
PARTITION BY 결과 집합을 파티션으로 나눠요. 함수는 파티션 안에서 독립적으로 적용돼요. 생략하면 전체 결과 집합이 하나의 파티션이 돼요
ORDER BY 파티션 안에서 행의 순서를 정의해요. 이 순서가 각 계산에 어떤 행이 프레임에 포함되는지 결정해요

기본 프레임

ORDER BY를 지정하면 기본 프레임은 이래요:

RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW

즉, 함수는 파티션 시작부터 현재 행까지(그리고 ORDER BY 값이 같은 행들까지 — 프레임 모드가 RANGE라서) 모든 행을 고려해요.

ORDER BY를 생략하면 기본 프레임이 파티션 전체를 덮어요.

윈도우 함수로 쓰는 집계 함수

아무 집계 함수든 OVER 절을 붙이면 윈도우 함수로 쓸 수 있어요.

함수 설명
count(*) 프레임 안의 행 수
count(expression) 프레임 안의 NULL이 아닌 값의 개수
sum(expression) 프레임 안의 NULL이 아닌 값의 합
avg(expression) 프레임 안의 NULL이 아닌 값의 평균
min(expression) 프레임 안의 최솟값
max(expression) 프레임 안의 최댓값
total(expression) REAL로 합산 (빈 프레임에서 NULL 대신 0.0을 반환해요)
group_concat(expression, separator) 프레임 안 값들의 이어 붙이기

랭킹 함수

row_number()

윈도우의 ORDER BY가 정의한 순서대로, 파티션 안의 각 행에 1부터 시작하는 연속 정수를 매겨요. Turso가 지원하는 유일한 전용 랭킹 함수예요.

SELECT
    department,
    name,
    salary,
    ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rank_in_dept
FROM employees;
department name salary rank_in_dept
Engineering Alice 90000 1
Engineering Bob 85000 2
Engineering Carol 75000 3
Marketing Dave 70000 1
Marketing Eve 60000 2

FILTER (WHERE ...)

FILTER (WHERE ...) 절은 집계 윈도우 함수가 어떤 행을 포함할지 좁혀주고, 쿼리가 반환하는 행에는 영향을 주지 않아요.

SELECT
    department,
    name,
    salary,
    SUM(salary) FILTER (WHERE salary > 70000) OVER (PARTITION BY department) AS high_earner_total
FROM employees;

PARTITION BY

PARTITION BY는 행들을 그룹으로 나눠요. 윈도우 함수는 파티션마다 독립적으로 초기화되고 다시 계산돼요.

SELECT
    department,
    name,
    salary,
    SUM(salary) OVER (PARTITION BY department) AS dept_total
FROM employees;
department name salary dept_total
Engineering Alice 90000 250000
Engineering Bob 85000 250000
Engineering Carol 75000 250000
Marketing Dave 70000 130000
Marketing Eve 60000 130000

PARTITION BY가 없으면 함수는 전체 결과 집합을 하나의 파티션으로 취급해요:

SELECT
    name,
    salary,
    SUM(salary) OVER () AS company_total
FROM employees;

ORDER BY

OVER 절 안의 ORDER BY는 각 파티션 안에서의 행 순서를 결정해요. 기본 프레임(RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)과 결합하면 누적 계산이 만들어져요.

SELECT
    name,
    salary,
    SUM(salary) OVER (ORDER BY salary) AS running_total
FROM employees;
name salary running_total
Eve 60000 60000
Dave 70000 130000
Carol 75000 205000
Bob 85000 290000
Alice 90000 380000

PARTITION BY와 ORDER BY 함께 쓰기

두 절을 함께 쓰면 그룹 안에서의 누적 계산을 할 수 있어요:

SELECT
    department,
    name,
    salary,
    SUM(salary) OVER (
        PARTITION BY department
        ORDER BY salary
    ) AS dept_running_total
FROM employees;
department name salary dept_running_total
Engineering Carol 75000 75000
Engineering Bob 85000 160000
Engineering Alice 90000 250000
Marketing Eve 60000 60000
Marketing Dave 70000 130000

이름 있는 윈도우 (Named Windows)

WINDOW 절은 재사용 가능한 윈도우 정의를 만들어서, 같은 쿼리 안의 여러 윈도우 함수가 참조할 수 있게 해줘요. 같은 OVER 정의를 반복 쓰는 걸 피할 수 있어요.

SELECT
    department,
    name,
    salary,
    SUM(salary) OVER w AS running_total,
    AVG(salary) OVER w AS running_avg,
    COUNT(*) OVER w AS running_count
FROM employees
WINDOW w AS (PARTITION BY department ORDER BY salary)
ORDER BY department, salary;

이름 있는 윈도우는 여러 개 정의할 수도 있어요:

SELECT
    department,
    name,
    salary,
    SUM(salary) OVER dept AS dept_total,
    SUM(salary) OVER company AS company_total
FROM employees
WINDOW
    dept AS (PARTITION BY department),
    company AS ()
ORDER BY department, name;

예제

누적 합계

SELECT
    order_date,
    amount,
    SUM(amount) OVER (ORDER BY order_date) AS running_total
FROM orders;

그룹별 개수

-- Show each employee alongside their department headcount
SELECT
    name,
    department,
    COUNT(*) OVER (PARTITION BY department) AS dept_size
FROM employees
ORDER BY department, name;

누적 평균

SELECT
    date,
    temperature,
    AVG(temperature) OVER (ORDER BY date) AS running_avg_temp
FROM weather_readings
ORDER BY date;

전체에서 차지하는 비율

SELECT
    product_name,
    revenue,
    ROUND(100.0 * revenue / SUM(revenue) OVER (), 2) AS pct_of_total
FROM products
ORDER BY revenue DESC;

한 쿼리에서 여러 윈도우 함수

SELECT
    department,
    name,
    salary,
    MIN(salary) OVER (PARTITION BY department) AS dept_min,
    MAX(salary) OVER (PARTITION BY department) AS dept_max,
    AVG(salary) OVER (PARTITION BY department) AS dept_avg,
    salary - AVG(salary) OVER (PARTITION BY department) AS diff_from_avg
FROM employees
ORDER BY department, salary DESC;

윈도우 위에서의 그룹 연결

SELECT
    department,
    name,
    GROUP_CONCAT(name, ', ') OVER (PARTITION BY department ORDER BY name) AS names_so_far
FROM employees;

제한 사항

다음 윈도우 함수 기능은 Turso에서 아직 지원되지 않아요:

기능 상태
rank() Not supported
dense_rank() Not supported
ntile(N) Not supported
lag(expr, offset, default) Not supported
lead(expr, offset, default) Not supported
first_value(expr) Not supported
last_value(expr) Not supported
nth_value(expr, N) Not supported
cume_dist() Not supported
percent_rank() Not supported
Custom frame: ROWS BETWEEN ... Not supported
Custom frame: RANGE BETWEEN ... AND ... Not supported
Custom frame: GROUPS BETWEEN ... Not supported
EXCLUDE clause Not supported

row_number()와 FILTER (WHERE ...)는 지원돼요 — 위 섹션을 참고하세요.

더 알아보기 (Learn more)

  • SELECT - WINDOW 절을 포함한 SELECT 전체 문법
  • 집계 함수 - 집계 함수 레퍼런스
  • 표현식 - 표현식 안에서 윈도우 함수 결과 사용하기