Oracle ORA-01847 오류 원인과 해결 방법 완벽 가이드

ORA-01847
2026년 08월 04일 | DBMS Error 가이드

이 글에서 다루는 내용

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

ORA-01847 day of month must be between 1 and last day of month 는?

ORA-01847 에러는 날짜 변환 또는 날짜 연산 과정에서 월(Month)의 일(Day) 값이 유효한 범위를 벗어났을 때 발생하는 Oracle 에러입니다. 예를 들어, 2월에 30일이나 31일을 지정하거나, 어떤 달이든 0일 혹은 음수 값을 일(Day)로 사용하면 이 에러가 발생합니다. 주로 TO_DATE, TO_TIMESTAMP, DATE 리터럴 사용 시 잘못된 날짜 문자열이 입력되었을 때 실무에서 자주 접하게 됩니다.


주요 발생 원인

1. TO_DATE 함수에서 잘못된 일(Day) 값 입력

가장 흔한 원인으로, 문자열을 날짜로 변환할 때 해당 월의 최대 일수를 초과한 값을 입력하는 경우입니다. 예를 들어 ‘2024-02-30’처럼 2월에 존재하지 않는 날짜를 TO_DATE로 변환하려 할 때 이 에러가 발생합니다. 사용자 입력값을 그대로 날짜 변환에 사용하는 애플리케이션에서 특히 자주 발생하며, 입력 데이터에 대한 사전 검증이 없는 경우 운영 환경에서도 쉽게 재현됩니다.

2. 배치 프로그램이나 ETL 작업에서 외부 소스 데이터의 날짜 오류

외부 시스템(ERP, 레거시 DB, CSV 파일 등)에서 가져온 데이터에 잘못된 날짜 값이 포함되어 있는 경우입니다. 특히 소스 시스템이 날짜 유효성 검사를 제대로 하지 않거나, 문자열 타입으로 날짜를 저장하다가 Oracle로 적재하는 과정에서 이 에러가 빈번하게 발생합니다. 대량 데이터 INSERT 또는 UPDATE 작업 중 단 하나의 레코드에만 잘못된 날짜가 있어도 전체 배치가 실패할 수 있어 영향 범위가 큽니다.

3. 동적 SQL 또는 문자열 연산으로 날짜를 조합할 때 발생하는 로직 오류

날짜를 동적으로 생성하는 PL/SQL이나 애플리케이션 로직에서 월말 계산, 분기 처리 등을 잘못 구현하면 유효하지 않은 날짜가 생성될 수 있습니다. 예를 들어 월(Month)에 1을 더하는 방식으로 다음 달을 계산하다가 12월에서 13월이 되거나, 특정 월의 마지막 날을 고정값으로 하드코딩하여 윤년 처리에 실패하는 경우가 대표적입니다. 이런 오류는 특정 날짜 조건에서만 재현되기 때문에 테스트 단계에서 발견하지 못하고 운영 환경에서 장애로 이어지는 경우가 많습니다.


해결 방법

원인 1 해결 – TO_DATE 사용 시 유효성 검사

잘못된 날짜 문자열이 입력되는 경우, 변환 전에 유효성을 확인하거나 예외 처리를 통해 안전하게 처리합니다.

-- 에러 발생 예시
SELECT TO_DATE('2024-02-30', 'YYYY-MM-DD') FROM DUAL;
-- ORA-01847 발생

-- 해결 방법 1: PL/SQL에서 예외 처리
DECLARE
    v_date DATE;
    v_str  VARCHAR2(20) := '2024-02-30';
BEGIN
    BEGIN
        v_date := TO_DATE(v_str, 'YYYY-MM-DD');
        DBMS_OUTPUT.PUT_LINE('유효한 날짜: ' || TO_CHAR(v_date, 'YYYY-MM-DD'));
    EXCEPTION
        WHEN OTHERS THEN
            DBMS_OUTPUT.PUT_LINE('유효하지 않은 날짜입니다: ' || v_str);
            -- 기본값 처리 또는 로그 기록
            v_date := NULL;
    END;
END;
/

-- 해결 방법 2: 유효 날짜 여부를 사전 확인하는 함수 생성
CREATE OR REPLACE FUNCTION is_valid_date(p_date_str IN VARCHAR2, p_format IN VARCHAR2)
RETURN VARCHAR2 IS
    v_date DATE;
BEGIN
    v_date := TO_DATE(p_date_str, p_format);
    RETURN 'Y';
EXCEPTION
    WHEN OTHERS THEN
        RETURN 'N';
END;
/

-- 함수 사용 예시
SELECT is_valid_date('2024-02-28', 'YYYY-MM-DD') AS valid_yn FROM DUAL;
-- 결과: Y
SELECT is_valid_date('2024-02-30', 'YYYY-MM-DD') AS valid_yn FROM DUAL;
-- 결과: N

원인 2 해결 – ETL/배치 데이터 적재 전 필터링

외부 소스 데이터를 적재하기 전 유효하지 않은 날짜 레코드를 사전에 분리하여 처리합니다.

-- 스테이징 테이블에서 유효하지 않은 날짜 데이터 식별
-- (날짜가 문자열로 저장된 경우)
SELECT stg_id, date_str_col
FROM   staging_table
WHERE  is_valid_date(date_str_col, 'YYYY-MM-DD') = 'N';

-- 유효한 데이터만 본 테이블에 INSERT
INSERT INTO target_table (id, reg_date, ...)
SELECT stg_id,
       TO_DATE(date_str_col, 'YYYY-MM-DD'),
       ...
FROM   staging_table
WHERE  is_valid_date(date_str_col, 'YYYY-MM-DD') = 'Y';

-- 유효하지 않은 데이터는 오류 테이블로 분리
INSERT INTO error_log_table (stg_id, error_col, error_msg, error_date)
SELECT stg_id,
       date_str_col,
       'ORA-01847: 유효하지 않은 날짜 값',
       SYSDATE
FROM   staging_table
WHERE  is_valid_date(date_str_col, 'YYYY-MM-DD') = 'N';

COMMIT;

원인 3 해결 – 동적 날짜 계산 로직 개선

월말 처리나 날짜 연산은 Oracle 내장 함수를 활용하여 안전하게 처리합니다.

-- 잘못된 방법: 월에 직접 숫자를 더하는 하드코딩 방식
-- 이 방법은 월말 처리 오류나 13월 문제 발생 가능
SELECT TO_DATE('2024-01-31', 'YYYY-MM-DD') + 30 AS next_date FROM DUAL;
-- 결과는 나오지만 의도와 다를 수 있음

-- 올바른 방법 1: ADD_MONTHS 함수 사용 (월말 자동 처리)
SELECT ADD_MONTHS(TO_DATE('2024-01-31', 'YYYY-MM-DD'), 1) AS next_month FROM DUAL;
-- 결과: 2024-02-29 (2024년은 윤년이므로 자동으로 말일 처리)

-- 올바른 방법 2: LAST_DAY 함수로 월의 마지막 날 구하기
SELECT LAST_DAY(TO_DATE('2024-02-01', 'YYYY-MM-DD')) AS last_day FROM DUAL;
-- 결과: 2024-02-29

-- 올바른 방법 3: TRUNC + ADD_MONTHS 조합으로 분기 처리
-- 현재 분기 시작일
SELECT TRUNC(SYSDATE, 'Q') AS quarter_start FROM DUAL;

-- 다음 분기 시작일
SELECT ADD_MONTHS(TRUNC(SYSDATE, 'Q'), 3) AS next_quarter_start FROM DUAL;

-- 특정 월의 마지막 일을 동적으로 구하는 예시
SELECT TO_CHAR(LAST_DAY(TO_DATE('2024-' || LPAD(LEVEL, 2, '0') || '-01', 'YYYY-MM-DD')), 'DD') AS last_day_of_month,
       LPAD(LEVEL, 2, '0') AS month_num
FROM   DUAL
CONNECT BY LEVEL <= 12;

예방 방법

1. 날짜 데이터는 반드시 DATE 또는 TIMESTAMP 타입 컬럼에 저장하라

테이블 설계 단계에서부터 날짜 관련 컬럼은 VARCHAR2가 아닌 DATE 또는 TIMESTAMP 타입으로 정의해야 합니다. 컬럼 타입 자체가 DATE이면 Oracle이 데이터 삽입 시점에 유효성을 자동으로 검사하므로 ORA-01847과 같은 에러를 데이터베이스 레벨에서 원천 차단할 수 있습니다. 또한, 애플리케이션에서 날짜를 문자열로 다루는 관행을 없애고, 파라미터 바인딩 시 날짜 타입으로 직접 전달하는 방식을 표준으로 삼아야 합니다.

2. 공통 날짜 변환 유틸리티 함수를 만들어 전사적으로 표준화하라

위에서 작성한 is_valid_date와 같은 공통 유틸리티 함수를 DBA 또는 개발 표준 패키지에 포함시켜 전체 시스템에서 재사용하도록 합니다. 날짜 변환이 필요한 모든 곳에서 TO_DATE를 직접 사용하는 대신 표준화된 함수를 거치도록 개발 가이드에 명시하면, 개발자 실수로 인한 날짜 에러를 구조적으로 예방할 수 있습니다. 아울러 CI/CD 파이프라인이나 배치 실행 전 단계에서 날짜 유효성 검사를 자동화하는 것도 권장합니다.


관련 에러

  • ORA-01843: not a valid month – 월(Month) 값이 유효하지 않을 때 발생합니다. 1~12 범위를 벗어난 월 값이나 잘못된 월 이름 문자열 사용 시 발생하며 ORA-01847과 함께 자주 나타납니다.
  • ORA-01858: a non-numeric character was found where a numeric was expected – 날짜 포맷 마스크와 실제 입력 문자열의 형식이 불일치할 때 발생합니다. TO_DATE의 포맷 형식과 데이터가 맞지 않을 경우 ORA-01847보다 먼저 이 에러가 발생하기도 합니다.
  • ORA-01839: date not valid for month specified – 지정한 월에 존재하지 않는 날짜를 사용했을 때 발생하며, ORA-01847과 매우 유사한 상황에서 나타납니다. Oracle 버전 및 내부 처리 경로에 따라 ORA-01847 대신 이 에러가 반환될 수 있습니다.
  • ORA-01841: (full) year must be between -4713 and +9999, and not be 0 – 연도 값이 허용 범위를 벗어났을 때 발생합니다. 날짜 유효성 에러 계열로 함께 다루어야 합니다.

DBMS 에러 코드 시리즈

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

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

댓글 남기기