2026년 08월 09일 | DBMS Error 가이드
이 글에서 다루는 내용
22015 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
22015 interval field overflow 는?
PostgreSQL 에러 코드 22015(interval field overflow)는 INTERVAL 타입의 값이 허용된 범위를 초과할 때 발생합니다. PostgreSQL의 INTERVAL 타입은 내부적으로 저장할 수 있는 최대/최소 범위가 정해져 있으며, 이를 벗어나는 연산이나 캐스팅을 시도할 경우 이 에러가 트리거됩니다. 주로 대용량 숫자를 인터벌로 변환하거나, 인터벌 간 산술 연산 중 오버플로우가 발생할 때 실무 환경에서 자주 마주치게 됩니다.
주요 발생 원인
- 극단적으로 큰 숫자를 INTERVAL로 변환 시도
PostgreSQL의 INTERVAL 타입은 내부적으로 월(months), 일(days), 마이크로초(microseconds) 세 가지 필드로 값을 저장합니다. 마이크로초 필드는 64비트 정수(int64)로 관리되므로, 이 범위를 초과하는 값(약 ±292,271년 이상)을 인터벌로 변환하려 하면 즉시 오버플로우 에러가 발생합니다. 예를 들어 '9999999999999 seconds'::interval처럼 천문학적인 초 단위 값을 변환하면 이 에러를 재현할 수 있습니다.
- INTERVAL 산술 연산 중 누적 오버플로우
두 개 이상의 INTERVAL 값을 더하거나, INTERVAL에 매우 큰 수를 곱하는 경우 중간 결과값이 내부 저장 한계를 초과할 수 있습니다. 특히 루프나 집계 함수 내에서 인터벌을 누적 합산하는 로직이 있을 경우 데이터 양이 많아질수록 오버플로우 위험이 높아집니다. 이런 상황은 로그 데이터나 시계열 데이터를 처리하는 배치 작업에서 자주 발생합니다.
- 외부 시스템에서 유입된 비정상적인 타임스탬프/인터벌 문자열 처리
ETL 파이프라인이나 외부 API를 통해 유입된 데이터에 비정상적으로 크거나 잘못된 형식의 인터벌 문자열이 포함될 수 있습니다. 이러한 값이 CAST 또는 ::interval 연산자를 통해 변환될 때 오버플로우가 발생하며, 데이터 품질 검증 없이 그대로 INSERT/UPDATE하면 배치 전체가 실패하는 상황으로 이어질 수 있습니다.
해결 방법
원인 1: 큰 숫자 변환 시 사전 범위 검증
변환 전에 값이 허용 범위 내에 있는지 확인하고, 범위를 벗어나는 경우 NULL 또는 기본값으로 처리합니다.
-- 오류 재현 예시
SELECT '9999999999999 seconds'::interval;
-- ERROR: interval field overflow
-- 해결: 변환 전 범위 체크 후 안전하게 처리
SELECT
CASE
WHEN abs(seconds_value) <= 9223372036 -- 약 292,271년 (안전 마진 포함)
THEN (seconds_value || ' seconds')::interval
ELSE NULL
END AS safe_interval
FROM (VALUES (9999999999999), (3600), (86400)) AS t(seconds_value);
-- 또는 예외 처리를 포함한 함수로 래핑
CREATE OR REPLACE FUNCTION safe_seconds_to_interval(p_seconds BIGINT)
RETURNS INTERVAL AS $$
BEGIN
RETURN (p_seconds || ' seconds')::INTERVAL;
EXCEPTION
WHEN interval_field_overflow THEN
RAISE WARNING 'interval_field_overflow for value: %', p_seconds;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
-- 사용 예시
SELECT safe_seconds_to_interval(3600); -- 00:01:00
SELECT safe_seconds_to_interval(99999999999999); -- NULL + WARNING
원인 2: 누적 인터벌 연산 시 중간값 제어
집계 연산 전에 개별 값을 클램핑하거나, 부분 합산 방식으로 오버플로우를 방지합니다.
-- 문제가 되는 패턴: 매우 큰 interval 누적 합산
-- SELECT SUM(duration_interval) FROM huge_log_table; -- 위험
-- 해결 1: 인터벌 대신 마이크로초(epoch) 단위로 집계 후 변환
SELECT
make_interval(secs => SUM(EXTRACT(EPOCH FROM duration_interval)))
FROM log_table
WHERE duration_interval IS NOT NULL;
-- 해결 2: 개별 값을 미리 클램핑하여 안전한 범위로 제한
SELECT SUM(
CASE
WHEN EXTRACT(EPOCH FROM duration_interval) > 315360000 -- 10년 초과 시 제한
THEN interval '10 years'
ELSE duration_interval
END
) AS clamped_total
FROM log_table;
-- 해결 3: INTERVAL을 곱할 때 오버플로우 방지
-- 잘못된 예: SELECT interval '1 day' * 9999999999;
-- 올바른 예:
SELECT make_interval(days => 365 * 100); -- 100년치 인터벌 안전하게 생성
원인 3: ETL 데이터 유입 시 방어 로직 추가
외부에서 유입된 데이터는 반드시 스테이징 테이블을 통해 검증 후 적재합니다.
-- 스테이징 테이블에서 안전한 값만 필터링하여 본 테이블에 적재
CREATE TABLE staging_events (
event_id BIGINT,
duration_raw TEXT -- 원본 문자열로 먼저 받음
);
-- 유효성 검증 후 안전한 행만 삽입
INSERT INTO events (event_id, duration)
SELECT
event_id,
safe_seconds_to_interval(duration_raw::BIGINT)
FROM staging_events
WHERE duration_raw ~ '^\d+$'
AND duration_raw::BIGINT BETWEEN 0 AND 9223372036;
-- 문제 행 별도 로깅
INSERT INTO error_log (source_table, raw_value, error_msg, logged_at)
SELECT
'staging_events',
duration_raw,
'interval_field_overflow risk: value out of range',
NOW()
FROM staging_events
WHERE duration_raw !~ '^\d+$'
OR duration_raw::BIGINT > 9223372036;
예방 방법
- 도메인 제약 조건(CHECK Constraint)으로 입력 범위 강제
테이블 설계 단계에서 CHECK 제약 조건을 통해 인터벌 컬럼에 허용 범위를 명시적으로 지정합니다. 이렇게 하면 애플리케이션 레이어의 버그나 잘못된 데이터 유입 시 데이터베이스 레벨에서 즉시 차단됩니다.
“`sql
CREATE TABLE task_logs (
id SERIAL PRIMARY KEY,
task_name VARCHAR(200) NOT NULL,
duration INTERVAL NOT NULL,
CONSTRAINT chk_duration_range
CHECK (duration >= interval ‘0’ AND duration <= interval '100 years')
);
— 기존 테이블에 제약 추가
ALTER TABLE task_logs
ADD CONSTRAINT chk_duration_range
CHECK (duration BETWEEN interval ‘0 seconds’ AND interval ‘100 years’);
“`
- 인터벌 연산이 포함된 함수/프로시저에 예외 핸들러 필수 탑재
INTERVAL 관련 연산을 포함하는 모든 PL/pgSQL 함수에는 EXCEPTION WHEN interval_field_overflow 블록을 반드시 추가합니다. 이를 통해 단일 레코드의 오류가 전체 배치 트랜잭션을 롤백시키는 상황을 방지하고, 오류 데이터를 별도 테이블에 기록하여 사후 분석이 가능하도록 합니다.
“`sql
CREATE OR REPLACE PROCEDURE process_duration_batch()
LANGUAGE plpgsql AS $$
DECLARE
r RECORD;
BEGIN
FOR r IN SELECT * FROM staging_events LOOP
BEGIN
INSERT INTO events (event_id, duration)
VALUES (r.event_id, (r.duration_raw || ‘ seconds’)::interval);
EXCEPTION
WHEN interval_field_overflow THEN
INSERT INTO error_log(event_id, raw_value, error_msg, logged_at)
VALUES (r.event_id, r.duration_raw, ‘interval_field_overflow’, NOW());
END;
END LOOP;
END;
$$;
“`
관련 에러
- 22003
numeric_value_out_of_range:INTERVAL과 함께 사용되는 숫자 필드가 범위를 초과할 때 발생하며, 22015와 함께 발생하는 경우가 많습니다. - 22008
datetime_field_overflow:TIMESTAMP,DATE타입의 값이 허용 범위를 벗어날 때 발생합니다. 인터벌 연산의 결과를 타임스탬프에 더하는 과정에서 22015 이후 연쇄적으로 발생할 수 있습니다. - 22007
invalid_datetime_format: 외부 데이터의 인터벌/날짜 문자열 형식이 잘못되었을 때 발생하며, ETL 파이프라인에서 22015와 함께 자주 등장하는 에러입니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.