PostgreSQL 22015 오류 원인과 해결 방법 완벽 가이드

22015
2026년 10월 13일 | DBMS Error 가이드

이 글에서 다루는 내용

22015 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.

22015 interval field overflow 는?

PostgreSQL 에러 코드 22015는 interval field overflow로, INTERVAL 데이터 타입의 값이 허용된 범위를 초과했을 때 발생합니다. PostgreSQL의 INTERVAL 타입은 내부적으로 microseconds(마이크로초) 단위로 값을 저장하며, 저장 가능한 최대 범위는 약 ±178,000,000년입니다. 이 범위를 넘어서는 연산이나 변환이 발생할 경우, 데이터베이스는 즉시 해당 에러를 발생시켜 잘못된 시간 계산이 시스템 전반으로 전파되는 것을 방지합니다.


주요 발생 원인

  • 극단적으로 큰 숫자를 INTERVAL로 변환하는 경우

가장 흔한 원인 중 하나로, 숫자 값을 직접 INTERVAL로 캐스팅하거나 make_interval() 함수에 매우 큰 정수를 전달할 때 발생합니다. 예를 들어, 외부 API나 레거시 시스템으로부터 받은 초 단위 타임스탬프 값이 비정상적으로 크거나, 프로그래밍 오류로 인해 잘못된 값이 넘어올 경우 이 에러가 자주 발생합니다. 특히 빅데이터 처리나 ETL 파이프라인에서 데이터 정합성 검증 없이 INTERVAL 변환을 수행할 때 실무에서 빈번하게 목격됩니다.

  • INTERVAL 값 간의 산술 연산으로 인한 누적 오버플로우

두 개 이상의 INTERVAL 값을 더하거나, INTERVAL에 큰 정수를 곱하는 연산 중에 결과 값이 허용 범위를 초과하는 경우 발생합니다. 예를 들어 루프 로직이나 집계(aggregation) 쿼리 내에서 INTERVAL 값들을 반복적으로 합산하다 보면 어느 순간 한계치를 넘어버릴 수 있습니다. 이 경우는 단순 입력 데이터 문제가 아니라 비즈니스 로직 자체의 설계 결함에서 비롯되는 경우가 많습니다.

  • 잘못된 문자열을 INTERVAL 타입으로 캐스팅하는 경우

문자열로 표현된 INTERVAL 값이 파싱은 성공했지만, 내부적으로 변환된 숫자 값이 허용 범위를 초과할 때 발생합니다. 예를 들어 '999999999 years'::interval처럼 문법적으로는 올바른 표현이지만 PostgreSQL 내부 한계를 초과하는 경우가 이에 해당합니다. 사용자 입력을 직접 INTERVAL로 변환하는 웹 애플리케이션에서 입력값 검증 없이 처리할 때 악의적이거나 실수에 의한 입력으로 이 에러가 발생할 수 있습니다.


해결 방법

원인 1: 큰 숫자를 INTERVAL로 변환하는 경우

변환 전에 값의 범위를 먼저 확인하고, 허용 가능한 범위 내에서만 변환을 수행하도록 방어 로직을 추가합니다.

-- 문제가 되는 쿼리 예시 (초 단위 값이 너무 큰 경우)
SELECT make_interval(secs => 9999999999999999);
-- ERROR: interval field overflow

-- 해결책 1: 변환 전 범위 검증
SELECT
  CASE
    WHEN some_seconds BETWEEN -15241578750190521 AND 15241578750190521
    THEN make_interval(secs => some_seconds)
    ELSE NULL  -- 또는 기본값 설정
  END AS safe_interval
FROM your_table;

-- 해결책 2: NULLIF와 GREATEST/LEAST를 이용한 범위 클램핑
SELECT make_interval(
  secs => GREATEST(-15241578750190521, LEAST(15241578750190521, some_seconds))
) AS clamped_interval
FROM your_table;

-- 해결책 3: 예외 처리를 포함한 PL/pgSQL 함수
CREATE OR REPLACE FUNCTION safe_make_interval(p_secs NUMERIC)
RETURNS INTERVAL AS $$
BEGIN
  RETURN make_interval(secs => p_secs);
EXCEPTION
  WHEN interval_field_overflow THEN
    RAISE WARNING 'Interval overflow detected for value: %', p_secs;
    RETURN NULL;
END;
$$ LANGUAGE plpgsql;

-- 함수 사용 예시
SELECT safe_make_interval(9999999999999999);

원인 2: INTERVAL 산술 연산으로 인한 오버플로우

집계나 반복 연산 시 중간값을 모니터링하고, 오버플로우가 예상되는 경우 데이터 타입을 변경하거나 로직을 분리합니다.

-- 문제가 되는 쿼리 예시 (INTERVAL 합산 시 오버플로우)
SELECT SUM(duration_interval) FROM event_log;
-- 데이터가 많을 경우 interval field overflow 발생 가능

-- 해결책 1: INTERVAL 대신 총 초(seconds)로 먼저 합산 후 변환
SELECT make_interval(secs => SUM(EXTRACT(EPOCH FROM duration_interval)))
FROM event_log;

-- 해결책 2: 큰 INTERVAL 곱셈 시 범위 확인 후 처리
SELECT
  CASE
    WHEN multiplier > 1000000 THEN NULL  -- 한계값 설정
    ELSE base_interval * multiplier
  END AS safe_result
FROM calculation_table;

-- 해결책 3: INTERVAL 곱셈을 epoch 기반으로 재작성
SELECT make_interval(
  secs => EXTRACT(EPOCH FROM base_interval) * multiplier
)
FROM calculation_table
WHERE multiplier <= 1000000;

-- 실제 INTERVAL 최대값 확인
SELECT make_interval(years => 178000000);  -- 약 최대값에 근접

원인 3: 잘못된 문자열 캐스팅

사용자 입력이나 외부 데이터를 INTERVAL로 변환하기 전 반드시 유효성 검사를 수행합니다.

-- 문제가 되는 쿼리 예시
SELECT '999999999 years'::interval;
-- ERROR: interval field overflow

-- 해결책 1: 정규식으로 연도 값 사전 검증
CREATE OR REPLACE FUNCTION validate_and_cast_interval(p_input TEXT)
RETURNS INTERVAL AS $$
DECLARE
  v_result INTERVAL;
BEGIN
  -- 입력값 기본 형식 검증
  IF p_input !~ '^\d+\s*(microseconds?|milliseconds?|seconds?|minutes?|hours?|days?|weeks?|months?|years?)$' THEN
    RAISE EXCEPTION 'Invalid interval format: %', p_input;
  END IF;

  BEGIN
    v_result := p_input::INTERVAL;
  EXCEPTION
    WHEN interval_field_overflow THEN
      RAISE EXCEPTION 'Interval value out of range: %', p_input;
    WHEN invalid_datetime_format THEN
      RAISE EXCEPTION 'Invalid interval string: %', p_input;
  END;

  RETURN v_result;
END;
$$ LANGUAGE plpgsql;

-- 함수 사용 예시
SELECT validate_and_cast_interval('5 years');     -- 정상
SELECT validate_and_cast_interval('999999999 years');  -- 에러 메시지 출력

-- 해결책 2: DO 블록으로 빠른 테스트
DO $$
DECLARE
  v_interval INTERVAL;
  v_input TEXT := '200000000 years';
BEGIN
  BEGIN
    v_interval := v_input::INTERVAL;
    RAISE NOTICE 'Success: %', v_interval;
  EXCEPTION
    WHEN interval_field_overflow THEN
      RAISE NOTICE 'Overflow detected, skipping value: %', v_input;
  END;
END;
$$;

예방 방법

  • 입력 데이터에 대한 CHECK 제약조건과 도메인 타입 활용

테이블 설계 단계에서부터 INTERVAL 컬럼에 합리적인 범위 제약을 걸어두면 오버플로우를 사전에 방지할 수 있습니다. PostgreSQL의 도메인(Domain) 기능을 활용하면 재사용 가능한 유효성 검사 규칙을 만들 수 있으며, 여러 테이블에 일관되게 적용할 수 있습니다.

“`sql

— 도메인 타입으로 안전한 INTERVAL 정의 (최대 100년으로 제한)

CREATE DOMAIN safe_interval AS INTERVAL

CHECK (

VALUE >= ‘-100 years’::INTERVAL

AND VALUE <= '100 years'::INTERVAL

);

— 도메인을 컬럼에 적용

CREATE TABLE project_timeline (

project_id SERIAL PRIMARY KEY,

project_name TEXT NOT NULL,

duration safe_interval NOT NULL,

created_at TIMESTAMPTZ DEFAULT NOW()

);

— 정상 삽입

INSERT INTO project_timeline (project_name, duration)

VALUES (‘프로젝트 A’, ‘2 years 3 months’);

— 범위 초과로 실패

INSERT INTO project_timeline (project_name, duration)

VALUES (‘프로젝트 B’, ‘200 years’);

— ERROR: value for domain safe_interval violates check constraint

— CHECK 제약조건을 기존 테이블에 추가하는 방법

ALTER TABLE existing_table

ADD CONSTRAINT chk_duration_range

CHECK (duration BETWEEN ‘-50 years’::INTERVAL AND ’50 years’::INTERVAL);

“`

  • ETL 파이프라인 및 애플리케이션 레이어에서의 사전 검증 함수 표준화

데이터가 PostgreSQL에 도달하기 전 단계에서 검증하는 것이 가장 효율적입니다. 공통 유틸리티 함수를 스키마 수준에서 관리하고, 모든 INTERVAL 변환 로직은 이 함수를 통해서만 수행되도록 코딩 가이드라인을 수립하세요. 또한 정기적인 데이터 품질 점검 쿼리를 스케줄링하여 이상값이 쌓이기 전에 조기 감지하는 체계를 갖추는 것이 중요합니다.

“`sql

— 공통 INTERVAL 안전 변환 함수 (스키마 레벨 표준화)

CREATE OR REPLACE FUNCTION util.safe_interval_from_seconds(

p_seconds NUMERIC,

p_default INTERVAL DEFAULT NULL,

p_max_years INTEGER DEFAULT 200

)

RETURNS INTERVAL AS $$

DECLARE

v_max_secs NUMERIC;

BEGIN

v_max_secs := p_max_years 365.25 24 * 3600;

IF p_seconds IS NULL THEN

RETURN p_default;

END IF;

IF ABS(p_seconds) > v_max_secs THEN

RAISE WARNING ‘[safe_interval] Value % exceeds % year limit, returning default’,

p_seconds, p_max_years;

RETURN p_default;

END IF;

RETURN make_interval(secs => p_seconds);

EXCEPTION

WHEN interval_field_overflow THEN

RAISE WARNING ‘[safe_interval] Overflow for value: %, returning default’, p_seconds;

RETURN p_default;

END;

$$ LANGUAGE plpgsql IMMUTABLE;

— 데이터 품질 모니터링 쿼리 (정기 실행 권장)

SELECT

COUNT(*) AS total_rows,

COUNT(duration) AS non_null_durations,

MIN(duration) AS min_duration,

MAX(duration) AS max_duration,

AVG(EXTRACT(EPOCH FROM duration)) AS avg_seconds

FROM project_timeline;

“`


관련 에러

  • 22007 (invalid_datetime_format): INTERVAL 문자열 파싱 자체가 실패할 때 발생합니다. 22015는 파싱 성공 후 범위를 초과할 때 발생하므로, 두 에러를 함께 처리하는 예외 핸들러를 작성하는 것이 좋습니다.
  • 22003 (numeric_value_out_of_range): 숫자 타입에서 발생하는 오버플로우 에러로, INTERVAL 변환에 사용되는 입력 숫자 값이 NUMERIC 범위를 초과할 때 선행하여 발생할 수 있습니다.
  • 22008 (datetime_field_overflow): DATE, TIMESTAMP 타입에서 유사하게 발생하는 오버플로우 에러입니다. INTERVAL 연산 결과를 TIMESTAMP에 더할 때 두 에러가 함께 발생할 수 있으므로 관련 로직에서는 두 에러 코드를 모두 처리해야 합니다.
DBMS 에러 코드 시리즈

주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.

본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.

댓글 남기기