2026년 06월 05일 | DBMS Error 가이드
이 글에서 다루는 내용
22015 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
22015 interval field overflow 는?
PostgreSQL 에러 코드 22015는 interval field overflow로, INTERVAL 데이터 타입의 값이 PostgreSQL이 허용하는 범위를 초과했을 때 발생합니다. PostgreSQL의 INTERVAL 타입은 내부적으로 microseconds 단위로 저장되며, 저장 가능한 최대 범위는 약 ±178,000,000년으로 매우 크지만, 특정 연산이나 형변환 과정에서 이 한계를 벗어나면 해당 에러가 트리거됩니다. 주로 대규모 시간 연산, 잘못된 문자열에서 INTERVAL로의 변환, 또는 EXTRACT/DATE_PART 함수와 함께 극단적인 날짜 값을 처리할 때 실무에서 자주 만나게 됩니다.
주요 발생 원인
1. INTERVAL 허용 범위를 초과하는 산술 연산
가장 흔한 원인으로, 두 개의 타임스탬프 또는 날짜 값의 차이를 계산할 때 결과가 PostgreSQL INTERVAL의 내부 저장 한계(microseconds 기준 약 ±9,223,372,036,854,775,807 microseconds)를 초과하는 경우 발생합니다. 특히 Unix Epoch 이전의 아주 오래된 날짜나 먼 미래의 날짜끼리 연산할 때 발생 빈도가 높습니다. 배치 처리나 데이터 마이그레이션 도중 원본 데이터의 날짜 컬럼에 비정상적인 값이 섞여 있을 경우 특히 주의가 필요합니다.
2. 잘못된 형식 또는 비정상적으로 큰 값을 INTERVAL로 캐스팅
문자열을 INTERVAL로 직접 캐스팅하거나 변환할 때, 문자열 내부의 숫자 값이 허용 범위를 벗어나면 이 에러가 발생합니다. 예를 들어 외부 API나 파일에서 수신한 데이터를 그대로 INTERVAL로 변환하는 ETL 파이프라인에서 자주 목격됩니다. 또한 사용자 입력값에 대한 유효성 검증 없이 동적 SQL로 INTERVAL 변환을 시도할 때도 동일한 문제가 생깁니다.
3. INTERVAL 정밀도(precision) 제한을 초과하는 값 처리
INTERVAL 타입은 선택적으로 정밀도와 필드 범위(YEAR TO MONTH, DAY TO SECOND 등)를 지정할 수 있는데, 이 제약 조건보다 큰 값을 삽입하거나 변환하려 할 때도 overflow가 발생합니다. 예를 들어 INTERVAL MINUTE(2) 컬럼에 1000분이 넘는 값을 저장하려 하면 해당 에러가 발생합니다. 테이블 DDL 설계 시 INTERVAL의 정밀도 설정을 충분히 고려하지 않으면 운영 환경에서 예상치 못한 에러로 이어질 수 있습니다.
해결 방법
원인 1: 범위 초과 산술 연산 해결
연산 전에 날짜 값의 유효성을 점검하거나, 결과를 NUMERIC으로 변환해서 처리하는 방식을 활용하세요.
-- 문제가 되는 쿼리 예시
SELECT '4714-11-24 BC'::timestamp - '9999-12-31'::timestamp;
-- ERROR: interval field overflow
-- 해결책 1: EXTRACT를 사용하여 epoch 초 단위로 직접 계산
SELECT EXTRACT(EPOCH FROM '9999-12-31'::timestamp)
- EXTRACT(EPOCH FROM '2000-01-01'::timestamp) AS diff_seconds;
-- 해결책 2: 연산 전에 날짜 범위를 제한하는 CASE 처리
SELECT
CASE
WHEN start_date < '0001-01-01'::date OR end_date > '9999-12-31'::date
THEN NULL
ELSE end_date - start_date
END AS safe_interval
FROM your_table;
-- 해결책 3: 이상치를 필터링하고 안전한 범위만 처리
SELECT
id,
end_date - start_date AS duration
FROM your_table
WHERE start_date BETWEEN '0001-01-01' AND '9999-12-31'
AND end_date BETWEEN '0001-01-01' AND '9999-12-31'
AND end_date >= start_date;
원인 2: 잘못된 캐스팅 해결
외부 데이터를 INTERVAL로 변환할 때는 반드시 예외 처리를 추가하거나, 변환 전에 값의 범위를 검증하세요.
-- 문제가 되는 쿼리 예시
SELECT '999999999 years'::interval;
-- ERROR: interval field overflow
-- 해결책 1: 안전한 변환을 위한 함수 래핑
CREATE OR REPLACE FUNCTION safe_to_interval(p_text TEXT)
RETURNS INTERVAL
LANGUAGE plpgsql
AS $$
BEGIN
RETURN p_text::interval;
EXCEPTION
WHEN interval_field_overflow THEN
RAISE WARNING 'interval field overflow for input: %', p_text;
RETURN NULL;
WHEN others THEN
RAISE WARNING 'Unexpected error for input: %, SQLSTATE: %', p_text, SQLSTATE;
RETURN NULL;
END;
$$;
-- 함수 사용 예시
SELECT safe_to_interval('999999999 years'); -- NULL 반환 + WARNING
SELECT safe_to_interval('10 days 3 hours'); -- 정상 반환
-- 해결책 2: 숫자 범위를 먼저 검증 후 INTERVAL 생성
DO $$
DECLARE
v_days INTEGER := 100;
BEGIN
IF v_days BETWEEN -3650000 AND 3650000 THEN
RAISE NOTICE 'interval: %', make_interval(days => v_days);
ELSE
RAISE WARNING 'Value out of safe range: %', v_days;
END IF;
END;
$$;
-- 해결책 3: make_interval 함수로 안전하게 생성
SELECT make_interval(years => 1000, months => 6, days => 15);
SELECT make_interval(hours => 500, mins => 30, secs => 45.5);
원인 3: INTERVAL 정밀도 제한 초과 해결
컬럼의 INTERVAL 타입 정의를 확인하고, 정밀도 제약을 완화하거나 저장 방식을 변경하세요.
-- 문제가 되는 DDL과 INSERT 예시
CREATE TABLE test_interval (
id SERIAL PRIMARY KEY,
duration INTERVAL MINUTE(2) -- 최대 99분까지만 허용
);
INSERT INTO test_interval (duration) VALUES ('1000 minutes');
-- ERROR: interval field overflow
-- 해결책 1: INTERVAL 정밀도 제약 완화 (컬럼 타입 변경)
ALTER TABLE test_interval
ALTER COLUMN duration TYPE INTERVAL;
-- 해결책 2: 더 큰 범위의 정밀도로 변경
ALTER TABLE test_interval
ALTER COLUMN duration TYPE INTERVAL MINUTE(6);
-- 해결책 3: 삽입 전 범위 검증 트리거 추가
CREATE OR REPLACE FUNCTION check_interval_range()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.duration > INTERVAL '99 minutes' THEN
RAISE EXCEPTION 'duration exceeds allowed maximum of 99 minutes. Got: %', NEW.duration;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_check_duration
BEFORE INSERT OR UPDATE ON test_interval
FOR EACH ROW EXECUTE FUNCTION check_interval_range();
-- 컬럼 정의 확인 방법
SELECT column_name, data_type, interval_type, interval_precision
FROM information_schema.columns
WHERE table_name = 'test_interval'
AND column_name = 'duration';
예방 방법
1. ETL 파이프라인 및 데이터 입력 단계에서 사전 유효성 검증 적용
외부 데이터를 PostgreSQL로 적재하기 전, 날짜 및 INTERVAL 관련 컬럼에 대해 반드시 범위 검증 로직을 포함하세요. 특히 배치 작업이나 API 연동 시에는 위에서 작성한 safe_to_interval() 같은 래퍼 함수를 공통 유틸리티로 등록하고 전 팀이 일관되게 사용하도록 표준화하는 것이 좋습니다. CHECK 제약 조건과 BEFORE 트리거를 조합하면 애플리케이션 레이어뿐 아니라 DB 레이어에서도 이중으로 방어할 수 있습니다.
-- CHECK 제약 조건으로 INTERVAL 범위 사전 제한
ALTER TABLE your_table
ADD CONSTRAINT chk_duration_range
CHECK (duration >= INTERVAL '0' AND duration <= INTERVAL '1000 years');
2. INTERVAL 대신 BIGINT(초 또는 밀리초)로 저장하는 전략 고려
매우 넓은 범위의 시간 차이를 다루는 시스템이라면 INTERVAL 타입 대신 초(seconds) 또는 밀리초(milliseconds) 단위의 BIGINT로 저장하는 방식을 고려하세요. BIGINT는 overflow 위험이 훨씬 낮고, 산술 연산도 훨씬 단순해집니다. 필요할 때만 make_interval(secs => bigint_value) 형태로 변환해서 사용하면 성능과 안정성을 모두 잡을 수 있습니다.
-- BIGINT로 duration을 초 단위로 저장하는 패턴
CREATE TABLE events (
id SERIAL PRIMARY KEY,
event_name TEXT,
duration_seconds BIGINT -- 초 단위로 저장
);
-- 조회 시 INTERVAL로 변환
SELECT event_name,
make_interval(secs => duration_seconds) AS duration_interval
FROM events;
관련 에러
- 22003 numeric_value_out_of_range: NUMERIC, INTEGER 등 숫자 타입에서 저장 범위를 초과할 때 발생하며, INTERVAL overflow와 유사한 패턴으로 발생합니다.
- 22007 invalid_datetime_format: 잘못된 형식의 문자열을 날짜/시간 타입으로 변환할 때 발생하며, INTERVAL 캐스팅 실패와 함께 자주 동반됩니다.
- 22008 datetime_field_overflow: DATE, TIMESTAMP 타입이 허용 범위를 초과할 때 발생하는 에러로, 22015와 함께 날짜/시간 관련 연산에서 쌍으로 발생하는 경우가 많습니다.
- 42804 datatype_mismatch: INTERVAL 컬럼에 다른 타입의 값을 잘못 할당하려 할 때 발생할 수 있으며, 형변환 에러와 함께 연계될 수 있습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.