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

ORA-01476
2026년 07월 18일 | DBMS Error 가이드

이 글에서 다루는 내용

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

ORA-01476 divisor is equal to zero 는?

ORA-01476은 SQL 또는 PL/SQL 코드에서 나눗셈 연산을 수행할 때 분모(제수, divisor)가 0이 되는 경우 Oracle 데이터베이스가 발생시키는 에러입니다. 수학적으로 어떤 수를 0으로 나누는 것은 정의되지 않은 연산이기 때문에, Oracle은 이를 런타임 에러로 처리합니다. 이 에러는 단순한 SELECT 쿼리부터 복잡한 PL/SQL 프로시저, 트리거, 함수에 이르기까지 나눗셈 연산이 포함된 모든 곳에서 발생할 수 있으며, 특히 데이터의 값이 동적으로 변하는 운영 환경에서 예기치 못하게 나타나는 경우가 많습니다.


주요 발생 원인

1. 컬럼 값이 0이거나 NULL인 경우를 고려하지 않은 나눗셈

가장 흔한 원인으로, 테이블의 특정 컬럼을 분모로 사용할 때 해당 컬럼에 0 또는 NULL이 저장되어 있는 경우입니다. 개발 초기에는 테스트 데이터에 0이 없어 정상 동작하다가, 운영 환경에서 실제 데이터가 쌓이면서 갑자기 에러가 발생하는 패턴이 매우 흔합니다. NULL의 경우 Oracle은 NULL을 0으로 처리하지 않지만, NVL 등으로 변환 시 0이 되어 에러를 유발할 수 있습니다.

2. 집계 함수(SUM, COUNT 등)의 결과를 분모로 사용하는 경우

GROUP BY 쿼리에서 SUM이나 COUNT 결과를 분모로 사용할 때, 특정 그룹의 집계 결과가 0이 되면 에러가 발생합니다. 예를 들어, 매출 대비 비율을 계산하는 리포트 쿼리에서 특정 월의 총 매출이 0이면 해당 행에서 ORA-01476이 발생합니다. 이 경우 에러가 산발적으로 나타나기 때문에 원인 파악이 더 어려울 수 있습니다.

3. PL/SQL 동적 계산 로직에서 변수가 0이 되는 경우

PL/SQL 블록 내에서 변수를 분모로 사용하는 나눗셈 연산을 수행할 때, 해당 변수가 특정 조건 분기를 거치면서 0이 되는 경우입니다. 특히 루프(LOOP) 또는 커서(CURSOR)를 이용한 반복 처리 중에 특정 레코드에서만 분모가 0이 되어 프로세스 전체가 중단되는 심각한 상황으로 이어질 수 있습니다. 이는 배치 작업에서 특히 치명적이며, 운영 중 장애로 이어지는 경우가 많습니다.


해결 방법

해결책 1: CASE WHEN 또는 NULLIF 함수 사용

가장 권장되는 방법은 NULLIF 함수를 사용하는 것입니다. NULLIF(expr1, expr2)는 두 값이 같으면 NULL을 반환하므로, 분모가 0일 때 NULL을 반환하게 하여 에러 대신 NULL 결과를 얻을 수 있습니다.

-- 문제가 되는 쿼리
SELECT employee_id,
       salary / work_hours AS hourly_rate
FROM   employees;

-- NULLIF를 사용한 해결
SELECT employee_id,
       salary / NULLIF(work_hours, 0) AS hourly_rate
FROM   employees;

-- CASE WHEN을 사용한 해결 (0일 때 기본값 반환)
SELECT employee_id,
       CASE
           WHEN work_hours = 0 OR work_hours IS NULL THEN 0
           ELSE salary / work_hours
       END AS hourly_rate
FROM   employees;

-- NVL과 NULLIF를 조합한 방법 (NULL 결과를 0으로 대체)
SELECT employee_id,
       NVL(salary / NULLIF(work_hours, 0), 0) AS hourly_rate
FROM   employees;

해결책 2: 집계 함수 결과를 분모로 사용하는 경우

-- 문제가 되는 집계 쿼리 (부서별 매출 비율 계산)
SELECT department_id,
       monthly_sales,
       monthly_sales / total_sales * 100 AS sales_ratio
FROM (
    SELECT department_id,
           SUM(sales_amount) AS monthly_sales,
           SUM(SUM(sales_amount)) OVER () AS total_sales
    FROM   sales
    WHERE  sale_month = '2024-01'
    GROUP  BY department_id
);

-- NULLIF를 적용한 안전한 집계 쿼리
SELECT department_id,
       monthly_sales,
       ROUND(monthly_sales / NULLIF(total_sales, 0) * 100, 2) AS sales_ratio
FROM (
    SELECT department_id,
           SUM(sales_amount)              AS monthly_sales,
           SUM(SUM(sales_amount)) OVER () AS total_sales
    FROM   sales
    WHERE  sale_month = '2024-01'
    GROUP  BY department_id
);

해결책 3: PL/SQL에서 예외 처리 및 사전 검증

-- PL/SQL에서 ZERO_DIVIDE 예외 처리
DECLARE
    v_salary      NUMBER := 5000000;
    v_work_hours  NUMBER := 0;
    v_hourly_rate NUMBER;
BEGIN
    -- 방법 1: 사전 조건 검사 (권장)
    IF v_work_hours = 0 OR v_work_hours IS NULL THEN
        DBMS_OUTPUT.PUT_LINE('작업 시간이 0이므로 시간당 급여를 계산할 수 없습니다.');
        v_hourly_rate := 0;
    ELSE
        v_hourly_rate := v_salary / v_work_hours;
        DBMS_OUTPUT.PUT_LINE('시간당 급여: ' || v_hourly_rate);
    END IF;

    -- 방법 2: EXCEPTION 블록으로 에러 처리
    BEGIN
        v_hourly_rate := v_salary / v_work_hours;
    EXCEPTION
        WHEN ZERO_DIVIDE THEN
            DBMS_OUTPUT.PUT_LINE('ORA-01476 발생: 분모가 0입니다. 기본값 0으로 처리합니다.');
            v_hourly_rate := 0;
        WHEN OTHERS THEN
            DBMS_OUTPUT.PUT_LINE('예상치 못한 에러: ' || SQLERRM);
            RAISE;
    END;
END;
/

-- 커서를 사용하는 배치 처리에서의 안전한 처리
DECLARE
    CURSOR c_emp IS
        SELECT employee_id, salary, work_hours
        FROM   employees
        WHERE  dept_id = 10;

    v_hourly_rate NUMBER;
BEGIN
    FOR r_emp IN c_emp LOOP
        BEGIN
            v_hourly_rate := r_emp.salary / NULLIF(r_emp.work_hours, 0);
            -- 추가 처리 로직
            DBMS_OUTPUT.PUT_LINE(
                'Employee ' || r_emp.employee_id ||
                ' 시간당 급여: ' || NVL(TO_CHAR(v_hourly_rate), 'N/A')
            );
        EXCEPTION
            WHEN ZERO_DIVIDE THEN
                DBMS_OUTPUT.PUT_LINE(
                    'Employee ' || r_emp.employee_id || ': 근무시간 0 - 건너뜀'
                );
                CONTINUE;
        END;
    END LOOP;
END;
/

예방 방법

1. 코딩 표준에 NULLIF 또는 CASE WHEN 패턴을 필수 규칙으로 지정

개발팀 내 SQL 코딩 표준 문서에 “나눗셈 연산 시 반드시 NULLIF(분모, 0) 패턴을 사용한다”는 규칙을 명시하고, 코드 리뷰 체크리스트 항목으로 포함시킵니다. 정적 분석 도구(SQL Developer의 Code Analysis 기능 등)를 활용하여 나눗셈 연산이 포함된 코드를 자동으로 검출하고, 분모 보호 로직이 없는 경우 경고를 발생시키는 프로세스를 구축하면 코드 품질을 체계적으로 관리할 수 있습니다.

2. 테이블 레벨에서 데이터 무결성 제약 조건 설정

분모로 사용될 컬럼에 CHECK 제약 조건을 추가하여 데이터베이스 레벨에서 0 값의 입력을 원천 차단합니다. 또한, 0이 유효한 비즈니스 값인 경우라면 해당 컬럼에 기본값(DEFAULT)을 설정하거나, 뷰(VIEW)를 통해 항상 안전한 계산 결과를 반환하도록 설계하여 애플리케이션이 항상 검증된 데이터를 사용하도록 구조화하는 것이 좋습니다.

-- CHECK 제약 조건으로 0 입력 방지
ALTER TABLE employees
ADD CONSTRAINT chk_work_hours_positive
CHECK (work_hours > 0 OR work_hours IS NULL);

-- 안전한 계산을 내포한 뷰 생성
CREATE OR REPLACE VIEW v_employee_hourly_rate AS
SELECT employee_id,
       salary,
       work_hours,
       NVL(salary / NULLIF(work_hours, 0), 0) AS hourly_rate
FROM   employees;

관련 에러

  • ORA-01422: EXACT FETCH RETURNS MORE THAN REQUESTED NUMBER OF ROWS — PL/SQL에서 INTO 절에 결과가 여러 건일 때 발생하며, ORA-01476과 마찬가지로 데이터 값에 의해 런타임에 발생하는 에러입니다.
  • ORA-06502: PL/SQL: numeric or value error — 숫자 연산 중 오버플로우, 타입 불일치 등으로 발생하는 에러로, ORA-01476과 함께 PL/SQL 수치 연산의 3대 에러로 꼽힙니다.
  • ORA-01428: argument '0' is out of range — 특정 내장 함수에 유효하지 않은 인수를 전달할 때 발생하며, 입력 데이터 검증 부재라는 공통 원인을 가집니다.

DBMS 에러 코드 시리즈

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

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

댓글 남기기