PostgreSQL 22013 오류: 원인과 해결책 완전 가이드

발행: (2026년 6월 9일 PM 03:02 GMT+9)
6 분 소요
원문: Dev.to

Source: Dev.to

umzzil nng

PostgreSQL 오류 22013: 윈도우 함수에서 PRECEDING 또는 FOLLOWING 크기 오류

PostgreSQL 오류 22013은 윈도우 함수 프레임의 PRECEDING 또는 FOLLOWING 절에 지정된 오프셋 값이 유효하지 않을 때 발생합니다. 일반적으로 오프셋이 음수이거나 NULL이거나 호환되지 않는 데이터 타입일 경우 발생합니다. 데이터베이스 엔진은 이러한 값으로 의미 있는 윈도우 프레임을 구성할 수 없으며 즉시 오류를 발생시킵니다.

주요 원인 3가지

  1. 음수 오프셋 값

    가장 흔한 원인은 프레임 오프셋으로 음수 정수를 전달하는 경우입니다. SQL 표준과 PostgreSQL 모두 PRECEDINGFOLLOWING 오프셋은 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;
  2. 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;
  3. ROWS 모드에서 비정수 오프셋 사용 또는 RANGE 모드에서 타입 불일치

    ROWS BETWEEN1.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 예제, 예방 팁이 포함되어 있습니다.

0 조회
Back to Blog

관련 글

더 보기 »

Eidentic 소개

Today we're releasing Eidentic, an open-source TypeScript SDK for building AI agents with self-improving memory and the production fundamentals built in — not b...

Typescript의 타입

Introdução Tipos são uma forma de definir a “forma” ou o contrato dos dados que estamos usando no código. Pensando em Javascript puro, ele é dinâmico: você pode...