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

ORA-06548
2026년 09월 01일 | DBMS Error 가이드

이 글에서 다루는 내용

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

ORA-06548 no more rows needed 는?

ORA-06548 에러는 Oracle의 파이프라인 테이블 함수(Pipelined Table Function) 또는 커서(CURSOR)를 사용하는 환경에서, 호출자(caller)가 더 이상 행(row)을 필요로 하지 않는다는 신호를 보냈을 때 발생하는 예외입니다. 주로 PIPE ROW 구문을 사용하는 파이프라인 함수 내부에서 조기 종료(early exit)가 요청될 때 나타나며, 이는 반드시 오류 상황이 아니라 정상적인 흐름 제어의 일부일 수도 있습니다. 그러나 이 예외를 적절히 처리하지 않으면 예상치 못한 프로그램 종료나 데이터 누락이 발생할 수 있으므로 DBA와 개발자 모두 정확히 이해하고 있어야 합니다.


주요 발생 원인

1. 파이프라인 함수(Pipelined Table Function)에서 예외 미처리

파이프라인 함수에서 PIPE ROW를 통해 행을 반환하는 도중, 쿼리의 WHERE 조건이나 ROWNUM, FETCH FIRST 등으로 인해 호출자가 조기에 커서를 닫을 경우 Oracle은 내부적으로 ORA-06548을 발생시킵니다. 이 예외를 함수 내에서 EXCEPTION 블록으로 잡아서 처리하지 않으면 에러가 외부로 전파되어 전체 쿼리가 실패할 수 있습니다.

2. ROWNUM 또는 FETCH FIRST 절과 파이프라인 함수의 조합

SELECT * FROM TABLE(my_pipelined_func()) WHERE ROWNUM <= 10 과 같이 파이프라인 함수 결과에 행 제한을 걸면, Oracle은 10개의 행을 가져온 즉시 더 이상 행이 필요 없다는 신호를 함수에 전달합니다. 이 시점에서 함수 내부가 여전히 PIPE ROW를 시도하면 ORA-06548이 발생하며, 이를 무시하거나 잘못 처리하면 쿼리 결과 자체가 비정상적으로 종료됩니다.

3. 중첩 파이프라인 함수 또는 커서 내에서의 비정상 종료 처리 부재

파이프라인 함수 안에서 또 다른 커서나 REF CURSOR를 열어 데이터를 처리하는 복잡한 구조에서, 내부 커서가 예외를 발생시키거나 호출자 측에서 커서를 강제로 닫는 경우 ORA-06548이 연쇄적으로 발생할 수 있습니다. 중첩 구조에서는 예외 전파 경로가 복잡해지므로 각 레이어에서 적절한 예외 처리 로직을 반드시 구현해야 합니다.


해결 방법

원인 1 해결: 파이프라인 함수 내 ORA-06548 예외 처리

파이프라인 함수의 EXCEPTION 블록에서 ORA-06548을 명시적으로 처리합니다.

CREATE OR REPLACE FUNCTION get_employee_data
RETURN employee_table_type PIPELINED
AS
    CURSOR emp_cur IS SELECT * FROM employees;
    v_emp employees%ROWTYPE;
BEGIN
    OPEN emp_cur;
    LOOP
        FETCH emp_cur INTO v_emp;
        EXIT WHEN emp_cur%NOTFOUND;
        PIPE ROW(employee_type(v_emp.employee_id, v_emp.first_name, v_emp.salary));
    END LOOP;
    CLOSE emp_cur;
    RETURN;
EXCEPTION
    WHEN NO_DATA_NEEDED THEN
        -- ORA-06548: 호출자가 더 이상 행을 필요로 하지 않을 때
        -- 열린 커서가 있다면 반드시 닫아준다
        IF emp_cur%ISOPEN THEN
            CLOSE emp_cur;
        END IF;
        RETURN; -- 정상 종료 처리
    WHEN OTHERS THEN
        IF emp_cur%ISOPEN THEN
            CLOSE emp_cur;
        END IF;
        RAISE;
END get_employee_data;
/

> 핵심 포인트: NO_DATA_NEEDED는 ORA-06548에 대한 Oracle이 제공하는 명명된 예외(named exception)입니다. 이를 활용하면 커서 누수(cursor leak) 없이 안전하게 함수를 종료할 수 있습니다.


원인 2 해결: ROWNUM 제한과 함께 사용하는 파이프라인 함수

-- 문제가 발생할 수 있는 쿼리 예시
SELECT *
FROM TABLE(get_employee_data())
WHERE ROWNUM <= 5;

-- 파이프라인 함수를 안전하게 구현한 전체 예시
CREATE OR REPLACE TYPE emp_rec_type AS OBJECT (
    emp_id   NUMBER,
    emp_name VARCHAR2(100),
    salary   NUMBER
);
/

CREATE OR REPLACE TYPE emp_table_type AS TABLE OF emp_rec_type;
/

CREATE OR REPLACE FUNCTION safe_employee_pipe
RETURN emp_table_type PIPELINED
AS
    v_emp_id   employees.employee_id%TYPE;
    v_name     employees.first_name%TYPE;
    v_salary   employees.salary%TYPE;
    
    CURSOR c_emp IS
        SELECT employee_id, first_name, salary FROM employees ORDER BY employee_id;
BEGIN
    OPEN c_emp;
    LOOP
        FETCH c_emp INTO v_emp_id, v_name, v_salary;
        EXIT WHEN c_emp%NOTFOUND;
        PIPE ROW(emp_rec_type(v_emp_id, v_name, v_salary));
    END LOOP;
    CLOSE c_emp;
    RETURN;
EXCEPTION
    WHEN NO_DATA_NEEDED THEN
        -- ROWNUM 또는 FETCH FIRST로 인해 조기 종료 시 안전하게 커서 정리
        IF c_emp%ISOPEN THEN
            CLOSE c_emp;
        END IF;
        RETURN;
END safe_employee_pipe;
/

-- 안전하게 행 제한을 적용한 쿼리
SELECT *
FROM TABLE(safe_employee_pipe())
FETCH FIRST 5 ROWS ONLY;

원인 3 해결: 중첩 커서 구조에서의 예외 처리

CREATE OR REPLACE FUNCTION nested_cursor_pipe
RETURN emp_table_type PIPELINED
AS
    CURSOR dept_cur IS SELECT department_id FROM departments;
    CURSOR emp_cur(p_dept_id NUMBER) IS
        SELECT employee_id, first_name, salary
        FROM employees
        WHERE department_id = p_dept_id;
    
    v_dept departments.department_id%TYPE;
    v_emp_id  employees.employee_id%TYPE;
    v_name    employees.first_name%TYPE;
    v_salary  employees.salary%TYPE;
BEGIN
    OPEN dept_cur;
    LOOP
        FETCH dept_cur INTO v_dept;
        EXIT WHEN dept_cur%NOTFOUND;
        
        OPEN emp_cur(v_dept);
        LOOP
            FETCH emp_cur INTO v_emp_id, v_name, v_salary;
            EXIT WHEN emp_cur%NOTFOUND;
            PIPE ROW(emp_rec_type(v_emp_id, v_name, v_salary));
        END LOOP;
        CLOSE emp_cur;
    END LOOP;
    CLOSE dept_cur;
    RETURN;
EXCEPTION
    WHEN NO_DATA_NEEDED THEN
        -- 중첩된 모든 커서를 안전하게 닫는다
        IF emp_cur%ISOPEN THEN
            CLOSE emp_cur;
        END IF;
        IF dept_cur%ISOPEN THEN
            CLOSE dept_cur;
        END IF;
        RETURN;
    WHEN OTHERS THEN
        IF emp_cur%ISOPEN THEN
            CLOSE emp_cur;
        END IF;
        IF dept_cur%ISOPEN THEN
            CLOSE dept_cur;
        END IF;
        RAISE;
END nested_cursor_pipe;
/

예방 방법

1. 모든 파이프라인 함수에 NO_DATA_NEEDED 예외 블록을 표준 템플릿으로 적용

파이프라인 함수를 개발할 때는 반드시 EXCEPTION WHEN NO_DATA_NEEDED 블록을 포함하는 코딩 표준을 팀 내에서 수립하고 코드 리뷰 체크리스트에 포함시켜야 합니다. 특히 함수 내에서 커서를 오픈하는 경우, NO_DATA_NEEDED 예외 블록 안에서 해당 커서를 닫는 정리 코드를 항상 함께 작성하여 커서 누수로 인한 ORA-01000(maximum open cursors exceeded) 에러가 연쇄적으로 발생하는 것을 방지해야 합니다.

2. 파이프라인 함수 테스트 시 행 제한 시나리오 반드시 포함

파이프라인 함수 단위 테스트(Unit Test)를 작성할 때는 전체 데이터를 조회하는 시나리오뿐만 아니라, ROWNUM, FETCH FIRST, WHERE 조건으로 중간에 조기 종료되는 시나리오를 반드시 포함해야 합니다. 이를 통해 운영 환경에서 예상치 못한 ORA-06548 에러가 발생하기 전에 개발 단계에서 사전에 검증할 수 있으며, 장기적으로는 Oracle의 DBMS_UTILITY 패키지나 자체 로깅 테이블을 활용하여 예외 발생 이력을 추적하는 것도 권장합니다.


관련 에러

  • ORA-01000: maximum open cursors exceededNO_DATA_NEEDED 예외를 처리하지 않아 커서가 누적될 경우 연쇄적으로 발생할 수 있습니다.
  • ORA-06503: PL/SQL: Function returned without value — 파이프라인 함수에서 RETURN 문 없이 종료될 때 발생하며, ORA-06548과 함께 나타나는 경우가 있습니다.
  • ORA-04068: existing state of packages has been discarded — 패키지 내 파이프라인 함수 사용 중 패키지가 재컴파일될 때 발생하며, 파이프라인 함수 오류와 혼동될 수 있습니다.
  • ORA-06512: at line N — ORA-06548의 스택 트레이스에서 자주 함께 등장하며, 에러 발생 위치를 추적하는 데 활용됩니다.
DBMS 에러 코드 시리즈

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

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

댓글 남기기