2026년 09월 08일 | DBMS Error 가이드
이 글에서 다루는 내용
42P20 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
42P20 windowing error 는?
PostgreSQL 에러 코드 42P20은 windowing error로, SQL 쿼리에서 윈도우 함수(Window Function)를 잘못 사용했을 때 발생하는 오류입니다. 윈도우 함수는 OVER() 절과 함께 사용되며, PARTITION BY, ORDER BY, 프레임 절(ROWS/RANGE) 등의 구문이 올바르지 않거나 서로 충돌할 때 이 에러가 발생합니다. 예를 들어 OVER 절 없이 윈도우 전용 함수를 사용하거나, 프레임 범위를 잘못 지정하는 등의 상황에서 PostgreSQL 파서나 플래너가 이 오류를 반환합니다.
주요 발생 원인
1. 잘못된 프레임 절(Frame Clause) 지정
윈도우 함수의 프레임 절에서 ROWS BETWEEN 또는 RANGE BETWEEN을 사용할 때, 시작값이 종료값보다 크게 지정되는 경우 이 에러가 발생합니다. 예를 들어 ROWS BETWEEN CURRENT ROW AND UNBOUNDED PRECEDING처럼 논리적으로 불가능한 범위를 설정하면 PostgreSQL은 즉시 42P20 오류를 반환합니다. 프레임 경계는 반드시 논리적으로 올바른 순서(시작 ≤ 종료)를 따라야 합니다.
2. OVER() 절과 집계 함수의 혼용 오류
일반 집계 함수(SUM, COUNT, AVG 등)와 윈도우 함수를 동일 쿼리 레벨에서 잘못 혼합하거나, 윈도우 함수를 WHERE, HAVING, GROUP BY 절 내부에 직접 사용하려 할 때 이 에러가 발생할 수 있습니다. 윈도우 함수는 쿼리의 결과 집합이 최종적으로 구성된 이후에 적용되므로, 필터링 단계에서 윈도우 함수를 직접 참조하는 것은 허용되지 않습니다. 이 경우 서브쿼리나 CTE(Common Table Expression)를 활용하여 단계를 분리해야 합니다.
3. RANGE 프레임과 ORDER BY 컬럼 타입 불일치
RANGE BETWEEN 프레임을 사용할 때는 ORDER BY 절의 컬럼이 수치형 또는 날짜형 등 범위 계산이 가능한 타입이어야 합니다. 만약 ORDER BY에 문자열(text) 컬럼을 지정하고 RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING처럼 오프셋을 주면, PostgreSQL은 타입 연산이 불가능하다고 판단하여 42P20 에러를 발생시킵니다. 이 경우 ROWS 프레임으로 변경하거나, ORDER BY 컬럼 타입을 수치형으로 변환해야 합니다.
해결 방법
원인 1: 잘못된 프레임 절 수정
에러 발생 쿼리:
-- 잘못된 프레임 절: CURRENT ROW 이후에 UNBOUNDED PRECEDING은 불가
SELECT
employee_id,
salary,
SUM(salary) OVER (
ORDER BY salary
ROWS BETWEEN CURRENT ROW AND UNBOUNDED PRECEDING
) AS running_total
FROM employees;
-- ERROR: 42P20: frame starting from current row cannot have preceding rows
수정된 쿼리:
-- 올바른 프레임 절: UNBOUNDED PRECEDING → CURRENT ROW 순서로
SELECT
employee_id,
salary,
SUM(salary) OVER (
ORDER BY salary
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_total
FROM employees;
프레임 절의 기본 원칙은 시작 경계 ≤ 종료 경계입니다. 아래는 올바른 프레임 절 조합 예시입니다:
-- 다양한 올바른 프레임 절 예시
SELECT
department_id,
employee_id,
salary,
-- 전체 파티션
AVG(salary) OVER (PARTITION BY department_id
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS dept_avg,
-- 누적 합계 (기본값과 동일)
SUM(salary) OVER (ORDER BY employee_id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS cumulative_sum,
-- 이동 평균 (앞뒤 1행 포함)
AVG(salary) OVER (ORDER BY employee_id
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS moving_avg
FROM employees;
원인 2: 윈도우 함수를 WHERE/HAVING 절에서 사용하는 경우
에러 발생 쿼리:
-- 윈도우 함수를 WHERE 절에서 직접 사용 불가
SELECT
employee_id,
salary,
RANK() OVER (ORDER BY salary DESC) AS rnk
FROM employees
WHERE RANK() OVER (ORDER BY salary DESC) <= 5;
-- ERROR: 42P20 또는 42803: window functions not allowed in WHERE clause
수정된 쿼리 (CTE 활용):
-- CTE를 활용하여 윈도우 함수 결과를 먼저 계산
WITH ranked_employees AS (
SELECT
employee_id,
department_id,
salary,
RANK() OVER (
PARTITION BY department_id
ORDER BY salary DESC
) AS salary_rank
FROM employees
)
SELECT
employee_id,
department_id,
salary,
salary_rank
FROM ranked_employees
WHERE salary_rank <= 5
ORDER BY department_id, salary_rank;
수정된 쿼리 (서브쿼리 활용):
-- 서브쿼리를 활용한 방법
SELECT *
FROM (
SELECT
employee_id,
salary,
ROW_NUMBER() OVER (
PARTITION BY department_id
ORDER BY salary DESC
) AS rn
FROM employees
) ranked
WHERE rn = 1; -- 각 부서에서 가장 높은 급여 직원 1명
원인 3: RANGE 프레임과 ORDER BY 타입 불일치 해결
에러 발생 쿼리:
-- 문자열 컬럼에 RANGE + 숫자 오프셋 사용 불가
SELECT
employee_name,
salary,
SUM(salary) OVER (
ORDER BY employee_name -- 문자열 타입
RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING -- 숫자 오프셋 불가
) AS group_sum
FROM employees;
-- ERROR: 42P20: RANGE with offset PRECEDING/FOLLOWING requires a single ORDER BY column of numeric or date/time type
수정 방법 1: ROWS 프레임으로 변경
-- RANGE 대신 ROWS 사용 (물리적 행 기준)
SELECT
employee_name,
salary,
SUM(salary) OVER (
ORDER BY employee_name
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
) AS group_sum
FROM employees;
수정 방법 2: 수치형 컬럼으로 ORDER BY 변경
-- ORDER BY 컬럼을 수치형으로 변경하여 RANGE 사용
SELECT
employee_id,
salary,
SUM(salary) OVER (
ORDER BY salary -- 수치형 컬럼
RANGE BETWEEN 1000 PRECEDING AND 1000 FOLLOWING
) AS salary_band_sum
FROM employees;
날짜 타입과 RANGE 조합 (올바른 예):
-- 날짜 타입 + INTERVAL 오프셋으로 RANGE 사용
SELECT
order_date,
amount,
SUM(amount) OVER (
ORDER BY order_date
RANGE BETWEEN INTERVAL '7 days' PRECEDING AND CURRENT ROW
) AS rolling_7day_sum
FROM orders;
예방 방법
1. 윈도우 함수 사용 전 프레임 절 기본 원칙 숙지 및 코드 리뷰 체계 구축
개발팀 내에서 윈도우 함수 사용 가이드라인을 문서화하고, 특히 프레임 절을 사용할 때는 반드시 페어 리뷰(Pair Review)를 통해 논리적 오류를 사전에 차단하세요. ROWS와 RANGE의 차이점, 오프셋 지정 시 ORDER BY 컬럼 타입 제약 등을 팀 전체가 이해하도록 내부 세션을 진행하는 것이 효과적입니다. 또한 개발 환경에서 EXPLAIN 또는 EXPLAIN ANALYZE를 통해 윈도우 함수 실행 계획을 미리 검증하는 습관을 들이세요.
-- 개발 단계에서 실행 계획 검증 예시
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT
department_id,
employee_id,
salary,
RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dept_rank,
SUM(salary) OVER (PARTITION BY department_id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS cumulative_dept_salary
FROM employees;
2. 복잡한 윈도우 함수 쿼리는 반드시 CTE로 단계 분리
윈도우 함수를 포함한 복잡한 쿼리는 CTE(WITH 절)를 사용하여 단계별로 분리하면 가독성이 높아질 뿐만 아니라 디버깅과 유지보수가 훨씬 쉬워집니다. 특히 윈도우 함수 결과를 필터링하거나, 여러 윈도우 함수 결과를 조합해야 할 때는 CTE를 적극 활용하세요. 이는 42P20 에러뿐만 아니라 42803(aggregation error) 등 관련 에러도 예방하는 데 효과적입니다.
-- 복잡한 분석 쿼리를 CTE로 단계 분리
WITH base_stats AS (
SELECT
department_id,
employee_id,
salary,
hire_date,
AVG(salary) OVER (PARTITION BY department_id) AS dept_avg_salary
FROM employees
),
ranked_stats AS (
SELECT
*,
RANK() OVER (
PARTITION BY department_id
ORDER BY salary DESC
) AS dept_salary_rank,
SUM(salary) OVER (
PARTITION BY department_id
ORDER BY hire_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS cumulative_payroll
FROM base_stats
)
SELECT
department_id,
employee_id,
salary,
dept_avg_salary,
dept_salary_rank,
cumulative_payroll
FROM ranked_stats
WHERE dept_salary_rank <= 10
ORDER BY department_id, dept_salary_rank;
관련 에러
42803(grouping_error):GROUP BY없이 집계 함수를 사용하거나, SELECT 절에 그룹화되지 않은 컬럼을 포함할 때 발생합니다. 윈도우 함수와 일반 집계 함수를 혼용할 때 함께 나타나는 경우가 많습니다.42P19(invalid_recursion): CTE를 재귀적으로 사용할 때 발생하는 에러로, 복잡한 윈도우 함수 쿼리를 CTE로 리팩토링하는 과정에서 재귀 CTE와 혼동할 때 발생할 수 있습니다.42601(syntax_error):OVER절의 문법 오류로 발생하며,42P20과 유사한 상황에서 파서 단계에서 먼저 검출되는 경우가 있습니다. 윈도우 함수 문법 자체가 잘못된 경우42P20이전에 이 에러가 먼저 반환됩니다.0A000(feature_not_supported): 특정 윈도우 함수 기능이 현재 PostgreSQL 버전에서 지원되지 않을 때 발생합니다. PostgreSQL 버전 업그레이드 이후 새로운 윈도우 함수 기능을 구버전에서 사용할 때 주의가 필요합니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.