이제 윈도우 함수 쓸 때 생길 수 있는 어려움에 대해 얘기해보자. 프로그래밍(그리고 인생)에서 남의 실수에서 배우는 게 최고잖아. 초보자뿐만 아니라 가끔은 경험 많은 개발자들도 저지르는 흔한 실수들을 같이 살펴보고, 어떻게 피할 수 있는지 배워보자.
실수 №1: PARTITION BY 잘못 쓰기
가장 자주 하는 실수 중 하나는 PARTITION BY 파라미터를 빼먹거나 잘못 지정하는 거야. 특히 데이터를 그룹별로 나누고 싶을 때 더 그렇지. 이걸 안 쓰면 PostgreSQL이 모든 행을 하나의 큰 그룹으로 보고, 결과가 기대와 완전 다르게 나올 수 있어.
예를 들어, sales라는 테이블이 있고, 여기에 판매 데이터가 들어있다고 해보자:
| id | region | month | total |
|---|---|---|---|
| 1 | North | 2023-01 | 1000 |
| 2 | South | 2023-01 | 800 |
| 3 | North | 2023-02 | 1200 |
| 4 | South | 2023-02 | 900 |
각 지역별로 월별 누적 합계(SUM())를 구하고 싶다고 해보자. 이렇게 쿼리를 쓸 수 있어:
SELECT
region,
month,
SUM(total) OVER (ORDER BY month) AS running_total
FROM
sales;
결과:
| region | month | running_total |
|---|---|---|
| North | 2023-01 | 1000 |
| South | 2023-01 | 1800 |
| North | 2023-02 | 3000 |
| South | 2023-02 | 3900 |
처음 보면 괜찮아 보이지만, 결과가 기대와 다르지? 누적 합계가 지역별로 계산된 게 아니라 모든 행을 한 번에 계산한 거야. PARTITION BY region를 빼먹은 게 문제지.
수정된 코드:
SELECT
region,
month,
SUM(total) OVER (PARTITION BY region ORDER BY month) AS running_total
FROM
sales;
결과:
| region | month | running_total |
|---|---|---|
| North | 2023-01 | 1000 |
| North | 2023-02 | 2200 |
| South | 2023-01 | 800 |
| South | 2023-02 | 1700 |
이제 제대로 됐지! 데이터가 지역별로 그룹화되고, 누적 합계도 각 지역마다 따로 계산돼.
실수 №2: ORDER BY 순서 잘못 지정하기
OVER() 안의 ORDER BY는 윈도우 내에서 행의 순서를 정해줘. 순서를 잘못 지정하면 결과가 엉뚱하게 나올 수 있어.
월별로 내림차순 정렬해서 누적 판매 합계를 구하고 싶다고 해보자. 이렇게 쿼리를 쓸 수 있어:
SELECT
month,
total,
SUM(total) OVER (ORDER BY month DESC) AS running_total
FROM
sales;
결과:
| month | total | running_total |
|---|---|---|
| 2023-02 | 1200 | 1200 |
| 2023-02 | 900 | 2100 |
| 2023-01 | 1000 | 3100 |
| 2023-01 | 800 | 3900 |
겉보기엔 맞는 것 같지만, 잘 보면 행이 월별로 묶여 있고 누적 합계가 내림차순 정렬 때문에 제대로 안 됐어. 그래서 결과가 헷갈리게 나와.
수정 방법: ORDER BY 순서를 올바르게 지정해줘:
SELECT
month,
total,
SUM(total) OVER (ORDER BY month ASC) AS running_total
FROM
sales;
실수 №3: 인덱스 없이 윈도우 함수 쓰기
윈도우 함수는 보통 데이터가 많을 때 쓰는데, 중요한 컬럼에 인덱스가 없으면 성능이 엄청 떨어질 수 있어.
예시: large_sales라는 테이블에 수백만 행이 있고, 판매 순위를 계산하고 싶다고 해보자:
SELECT
id,
total,
RANK() OVER (ORDER BY total DESC) AS rank
FROM
large_sales;
데이터가 적으면 금방 끝나지만, 많으면 진짜 오래 걸릴 수 있어.
수정 방법: ORDER BY에 쓰는 컬럼에 인덱스를 추가해줘:
CREATE INDEX idx_total ON large_sales(total DESC);
이제 쿼리가 훨씬 빨라질 거야.
실수 №4: ROWS나 RANGE 윈도우 범위 헷갈리기
ROWS랑 RANGE 쓸 때, 이게 어떻게 윈도우 범위를 정하는지 잘 이해해야 해. 이걸 잘못 알면 결과가 이상하게 나올 수 있어.
예시: 이번 달과 이전 두 달의 이동 평균을 구하고 싶다고 해보자:
SELECT
month,
AVG(total) OVER (ORDER BY month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM
sales;
만약 ROWS 대신 RANGE를 쓰면:
SELECT
month,
AVG(total) OVER (ORDER BY month RANGE BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM
sales;
결과가 달라질 거야. RANGE는 값의 범위로 계산하고, ROWS는 행의 개수로 계산하거든.
실수 №5: 윈도우 함수 남발하기
한 쿼리에서 여러 번 윈도우 함수를 쓰면 계산이 중복되고, 성능도 떨어질 수 있어.
예시:
SELECT
id,
total,
SUM(total) OVER (PARTITION BY region) AS region_total,
SUM(total) OVER (PARTITION BY region) / COUNT(total) OVER (PARTITION BY region) AS region_avg
FROM
sales;
여기서 SUM(total)이랑 COUNT(total)가 각 행마다 여러 번 계산돼.
수정 방법: 서브쿼리나 CTE를 써서 쿼리를 줄여보자:
WITH cte_region_totals AS (
SELECT
region,
SUM(total) AS region_total,
COUNT(total) AS region_count
FROM
sales
GROUP BY
region
)
SELECT
s.id,
s.total,
t.region_total,
t.region_total / t.region_count AS region_avg
FROM
sales s
JOIN
cte_region_totals t ON s.region = t.region;
실수 피하는 꿀팁
PARTITION BY랑 ORDER BY 꼭 확인하기: 윈도우가 제대로 지정됐는지 항상 체크해.
데이터에 인덱스 걸기: 특히 ORDER BY나 필터링 쓸 때는 필수야.
중복 계산엔 CTE 쓰기: 중복되는 계산 줄이려면 CTE가 좋아.
실행 계획 보기: EXPLAIN이나 EXPLAIN ANALYZE로 PostgreSQL이 쿼리를 어떻게 처리하는지 확인해봐.
실제 데이터로 테스트하기: 결과가 기대와 맞는지, 문제 없이 동작하는지 꼭 확인해봐.
GO TO FULL VERSION