PostgreSQL 22013 오류: 원인과 해결책 완전 가이드
Source: Dev.to
PostgreSQL 오류 22013: 윈도우 함수에서 PRECEDING 또는 FOLLOWING 크기 오류
PostgreSQL 오류 22013은 윈도우 함수 프레임의 PRECEDING 또는 FOLLOWING 절에 지정된 오프셋 값이 유효하지 않을 때 발생합니다. 일반적으로 오프셋이 음수이거나 NULL이거나 호환되지 않는 데이터 타입일 경우 발생합니다. 데이터베이스 엔진은 이러한 값으로 의미 있는 윈도우 프레임을 구성할 수 없으며 즉시 오류를 발생시킵니다.
주요 원인 3가지
-
음수 오프셋 값
가장 흔한 원인은 프레임 오프셋으로 음수 정수를 전달하는 경우입니다. SQL 표준과 PostgreSQL 모두
PRECEDING및FOLLOWING오프셋은 0 또는 양의 정수여야 합니다.-- ERROR: 음수 오프셋 때문에 22013 발생 SELECT employee_id, salary, AVG(salary) OVER ( ORDER BY hire_date ROWS BETWEEN -2 PRECEDING AND CURRENT ROW -- 잘못된 구문! ) AS avg_salary FROM employees; -- FIXED: 음수가 아닌 정수를 사용 SELECT employee_id, salary, AVG(salary) OVER ( ORDER BY hire_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW ) AS avg_salary FROM employees; -
NULL 오프셋 값
오프셋을 동적으로 계산할 때(예: 서브쿼리, 파라미터, 표현식)
NULL이 될 수 있으며, 이 경우 오류 22013이 발생합니다.-- ERROR: NULL 오프셋 때문에 22013 발생 WITH cfg AS ( SELECT NULL::INTEGER AS win_size ) SELECT order_id, amount, SUM(amount) OVER ( ORDER BY order_date ROWS BETWEEN (SELECT win_size FROM cfg) PRECEDING AND CURRENT ROW ) AS running_total FROM orders; -- FIXED: COALESCE를 사용해 안전한 기본값 제공 WITH cfg AS ( SELECT COALESCE(NULL::INTEGER, 7) AS win_size ) SELECT order_id, amount, SUM(amount) OVER ( ORDER BY order_date ROWS BETWEEN (SELECT win_size FROM cfg) PRECEDING AND CURRENT ROW ) AS running_total FROM orders; -
ROWS 모드에서 비정수 오프셋 사용 또는 RANGE 모드에서 타입 불일치
ROWS BETWEEN에1.5와 같은 소수값을 사용하거나,RANGE BETWEEN에서ORDER BY열과 타입이 맞지 않는 오프셋을 제공하면 오류가 발생합니다.-- ERROR: ROWS 모드에서 소수 오프셋 SELECT sale_id, amount, SUM(amount) OVER ( ORDER BY sale_date ROWS BETWEEN 1.5 PRECEDING AND CURRENT ROW -- 잘못된 구문! ) AS total FROM sales; -- FIXED (ROWS 모드): 정수를 사용 SELECT sale_id, amount, SUM(amount) OVER ( ORDER BY sale_date ROWS BETWEEN 1 PRECEDING AND CURRENT ROW ) AS total FROM sales; -- FIXED (RANGE 모드): 오프셋 타입을 ORDER BY 열 타입에 맞춤 SELECT sale_id, sale_date, amount, SUM(amount) OVER ( ORDER BY sale_date RANGE BETWEEN INTERVAL '7 days' PRECEDING AND CURRENT ROW ) AS weekly_total FROM sales;
빠른 해결 방법
- 동적 오프셋을 윈도우 함수에 전달하기 전에 항상 검증하세요.
GREATEST(computed_value, 0)을 사용해 음수가 되지 않도록 보장합니다.COALESCE(computed_value, fallback_integer)를 사용해NULL을 처리합니다.
윈도우 함수 로직을 입력 검증이 포함된 저장 함수로 캡슐화하는 방법:
-- 방어적인 헬퍼 패턴
CREATE OR REPLACE FUNCTION safe_moving_avg(p_days INTEGER DEFAULT 7)
RETURNS TABLE(sale_date DATE, moving_avg NUMERIC) AS $$
BEGIN
IF p_days IS NULL OR p_days < 0 THEN
RAISE EXCEPTION '윈도우 크기는 음수가 될 수 없으며, 입력값: %', p_days;
END IF;
RETURN QUERY
SELECT
s.sale_date,
AVG(s.amount) OVER (
ORDER BY s.sale_date
ROWS BETWEEN p_days PRECEDING AND CURRENT ROW
)
FROM sales s
ORDER BY s.sale_date;
END;
$$ LANGUAGE plpgsql;
예방 팁
-
애플리케이션 또는 함수 레이어에서 모든 동적 윈도우 오프셋을 검증하세요. 계산된 값이 유효한 양의 정수라는 것을 절대 가정하지 마세요.
GREATEST(..., 0)와COALESCE(...)를 일관되게 적용하고, 잘못된 입력에 대해서는 PostgreSQL이 모호한 오류를 내보내기보다 의미 있는 예외를 직접 발생시키는 것이 좋습니다. -
팀의 SQL 코딩 표준에 ROWS와 RANGE 사용 규칙을 문서화하고 강제하세요.
ROWS모드에서는 정수 오프셋만 허용하고,RANGE모드에서는 오프셋 타입이ORDER BY열 타입과 일치해야 한다는 규칙을 명시하세요. 이 규칙을 코드 리뷰 체크리스트에 포함하면 프로덕션 환경에서 오류 22013이 발생할 확률을 크게 줄일 수 있습니다.
관련 오류
- 42P20 (windowing_error) – 윈도우 함수 정의 자체가 구조적으로 잘못되었을 때 발생합니다.
- 22000 (data_exception) – 22013을 포함한 데이터 관련 예외들의 상위 오류 클래스입니다.
- 42883 (undefined_function) –
RANGE모드에서 오프셋 타입 조합에 맞는 연산자가 없을 때 발생할 수 있습니다.
📖 더 자세한 가이드를 원하시나요?
전체 심층 버전(한국어)을 oraerror.com에서 확인하세요 — 자세한 분석, 추가 SQL 예제, 예방 팁이 포함되어 있습니다.
