예외 처리 방법
오라클 PL/SQL에서는 EXCEPTION절을 사용한다.
BEGIN
....
EXCEPTION WHEN 예외명 THEN
-- 예외 처리문
...
END;
예외 처리를 하지 않는 경우에는 실행되던 SQL이나 PL/SQL이 즉시 종료되고 사용자에게는 오류가 전달된다. 실제 프로그램 작성 시에는 특정 예외가 발생하면 이에 적절한 처리를 하고 프로그램을 계속하는 경우가 많다. 예외 처리가 없다면 의도하지 않은 위치나 시점에서 프로그램이 종료되어 버리므로 예외 처리 없이 본격적인 PL/SQL 프로그램을 작성하는 것은 거의 어렵다고 볼 수 있다.
예외 처리를 사용하면 여러 문장에서 발생하는 예외를 하나의 예외 처리기로 대응할 수 있다. 여러 개의 문장(SQL문과 PL/SQL문을 모두 포함)이 있을 경우 발생하는 예외를 문장마다 개별로 처리하는 것은 매우 어려운 일이다. 예외 처리기는 여러 개의 문장에 대해서도 한 번에 예외 처리를 할 수 있다. 문장이 한 프로그램에 있건 호출되는 다른 서브프로그램에 들어 있건 상관없이 실행되는 모든 문장에 대해 예외 처리가 가능하다. 예외처리도 중첩이 가능하기 때문에 100개의 문장 중에서 특별히 다르게 예외 처리를 해야 할 문장이 하나가 있다면 이 문장을 중첩된 별도의 BEGIN-EXCEPTION-END블록으로 만들어서 예외처리를 할 수 있다.
예외의 이름
표준 예외명
오라클에서 발생하는 ORA-XXXXX 오류 중에서 자주 발생하는 오류들을 예외로 미리 정의한 것이다.
표준 예외명은 내장 패키지 STANDARD에 선언되어 있는데 버전 12c 기준으로 24개가 정의되어 있다.
*(): 오라클 오류코드
-ACCESS_INTO_NULL(-6530): 초기화되지 않은 컴포지트 변수에 값을 할당하거나 SELECT INTO 사용
-CASE_NOT_FOUND(-6592): 모든 CASE를 나열하거나 ELSE절을 사용해야 함
-COLLECTION_IS_NULL(-6531): 초기화되지 않은 VARRAY 혹은 Nested Table을 참조
-CURSOR_ALREADY_OPEN(-6511): 이미 열린 커서를 다시 OPEN하려고 함
-DUP_VAL_ON_INDEX(-1): UNIQUE 제약조건 위반
-INVALID_CURSOR(-1001): 유효하지 않은 커서(정상적으로 OPEN되지 않은 커서 사용)
-INVALID_NUMBER(-1722): 올바른 숫자가 아님(숫자에 숫자가 아닌 문자를 사용)
-LOGIN_DENIED(-1017): 계정 또는 암호가 올바르지 않아서 로그인할 수 없음
-NO_DATA_FOUND(+100): 더 이상의 데이터가 존재하지 않아 데이터가 FETCH되지 않음
-NO_DATA_NEEDED(-6548): 파이프라인(PIPELINED) 함수 호출자가 더 이상의 로우를 필요로 하지 않음
-NOT_LOGGED_ON(-1012): 로그온되지 않음
-OTHERS(N/A): 발생하는 어떤 예외라도 처리할 수 있는 예외 처리기를 정의
-PROGRAM_ERROR(-6501): 오라클 내부적인 오류 발생
-ROWTYPE_MISMATCH(-6504): 쿼리의 결과로 반환되는 컬럼의 개수 또는 데이터타입이 결과 변수의 개수 또는 데이터
타입과 일치 또는 호환되지 않음
-SELF_IS_NULL(-30625): 객체 타입의 멤버 변수에 NULL인 SELF 매개변수가 사용됨
-STORAGE_ERROR(-6500): PL/SQL에서 추가적인 저장소를 할당하지 못했음
-SUBSCRIPT_BEYOND_COUNT(-6533): VARRAY 혹은 Nested Table에서 현재 데이터의 상/하한 범위를 벗어난 배열
인덱스가 사용됨(프로그램 Logic 오류)
-SUBSCRIPT_OUTSIDE_LIMIT(-6532): VARRAY 혹은 Nested Table에서 제한된 건수 범위를 벗어난 배열 인덱스가 사용됨
-SYS_INVALID_ROWID(-1410): 유효하지 않은 ROWID가 사용됨
-TIMEOUT_ON_RESOURCE(-51): 리소스 대기 중 타임아웃 발생(주로 인스턴스 오류로 인해 발생)
-TOO_MANY_ROWS(-1422): 결과 변수에 지정된 반환 로우 수보다 더 많은 로우가 반환됨
-USERENV_COMMITSCN_ERROR(-1725): USERENV('COMMITSCN')가 사용될 수 없는 곳에 사용됨
-VALUE_ERROR(-6502): 연산, 수치, 문자열, 변환, 제약조건에 관련된 오류 발생
-ZERO_DIVIDE(-1476): 나눗셈의 분모가 0이다.
아마도 많이 사용되는 표준 예외명은 DUP_VAL_ON_INDEX, NO_DATA_FOUND, TOO_MANY_ROWS, OTHERS일것이다.
사용자 정의 예외명
표준 예외명 중 OTHERS를 제외한 예외명은 특정 오류 코드에 일대일로 대응되어 있다. 표준 예외로 정의되지 않은 예외에 대해 예외명을 사용하고자 할 때에는 사용자 정의 예외명을 사용할 수 있다. 사용자 정의 예외명은 DECLARE 블록에서 키워드 EXCEPTION을 사용하여 선언되어야 한다.
DECLARE
no_emp_found EXCEPTION; -- 사용자 지정 예외 선언
v_cnt PLS_INTEGER;
v_empno emp.empno%TYPE;
BEGIN
v_empno := -1; -- 존재하지 않는 사번
-- 사원이 존재하는지 확인
SELECT COUNT(*) INTO v_cnt
FROM emp
WHERE EMPNO = v_empno;
IF v_cnt = 0 THEN -- 사원이 존재하지 않으면 사용자 예외 발생
RAISE no_emp_found;
END IF;
UPDATE emp
SET SAL = SAL * 1.1
WHERE EMPNO = v_empno;
EXCEPTION WHEN no_emp_found THEN -- 사용자 지정 예외를 처리
DBMS_OUTPUT_PUT_LINE('처리할 사원이 존재하지 않습니다');
END;
사용자가 예외를 발생시키기
예외는 SQL문이나 PL/SQL의 수행 주에 내부적으로 발생할 수 있고 사용자가 인위적으로 발생시킬 수도 있다.
RAISE문 사용
DECLARE 블록에서 예외를 사전에 선언 후 BEGIN ~ END 사이의 블록에서 RAISE문을 사용하여 예외를 발생시킨다.
DECLARE
v_deptno dept.deptno%TYPE := 30;
v_empno emp.empno%TYPE := 7900;
v_dname dept.dname&TYPE;
v_ename emp.ename%TYPE;
BEGIN
SELECT MAX(dname) dname
INTO v_dname
FROM dept
WHERE deptno = v_deptno;
IF v_dname IS NULL THEN
RAISE_APPLICATION_ERROR(-20001, '부서 번호 '||v_deptno||'이 존재하지 않습니다.');
END IF;
BEGIN
SELECT ename
INTO v_ename
FROM emp
WHERE empno = v_empno;
EXCEPTION WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20002, '사원 번호 '||v_ename||'이 존재하지 않습니다.'):
END;
DBMS_OUTPUT_PUT_LINE('부서와 사원 데이터에 이상이 없습니다.');
EXCEPTION WHEN OTHERS THEN
IF SQLCODE = -20001 THEN
DBMS_OUTPUT_PUT_LINE('부서 오류');
DBMS_OUTPUT_PUT_LINE(SQLERRM);
ELSIF SQLCODE = -20002 THEN
DBMS_OUTPUT_PUT_LINE('사원 오류');
DBMS_OUTPUT_PUT_LINE(SQLERRM);
END IF;
END;
위 예제에서는 예외 처리기에서 예외명 OTHERS를 잡은 후 예외 코드 값에 따라 예외처리를 하고 있는데, 예외명 없이 처리하다 보니 썩 매끄러워 보이지는 않는다. 위와 다르게 사용자 지정 예외번호를 특정 예외명과 연결하여 사용하면 좀 더 깔끔한 코드를 만들 수 있다.
예외를 특정 오류 번호와 연결하기
오라클 표준 예외 중에서 OTHERS를 제외한 다른 예외들은 오라클 SQL이나 내장 패키지 또는 PL/SQL에서 발생하는 오류번호와 연결되어 있다. 사용자 지정 예외는 표준 예외와 같이 예외를 하나의 오라클 오류와 연결하고 이에 이름을 부여하는 기능을 제공한다. 사용자 지정 예외명을 특정 오라클 오류와 연결하기 위해서는 PRAGMA EXCEPTION_INIT을 사용한다.
DECLARE
invalide_date EXCEPTION; -- 사용자 지정 예외
PRAGMA EXCEPTION_INIT(invalide_date, -1847); -- ORA-01847과 연결
-- ORA-01847: 달의 날짜는 1에서 말일 사이어야 합니다.
v_date DATE;
BEGIN
-- 오류 유발
v_date := TO_DATE('2000-12-32', 'YYYY-MM-DD');
EXCEPTION
WHEN invalide_date THEN
DBMS_OUTPUT_PUT_LINE('날짜 오류가 검출되었습니다.');
END;
예외의 전파
기본적으로 예외는 블록 단위로 제어된다. 한 블록에서 발생하는 예외는 해당 블록에 EXCEPTION WHEN을 사용하는 예외 처리기가 존재하는 경우 해당 예외 처리기에 의해 처리된다. 해당 블록이 예외를 처리하지 않는 경우에는 예외가 그 블록을 감싸는 바로 상위 블록으로 전파된다. 상위 블록이 예외를 처리하지 않으면 다시 그 상위 블록으로 전파되며 최상위 블록도 예외를 처리하지 않으면 최상위 블록을 호출한 사용자에게 오류를 전달하면서 프로그램이 종료된다.
블록 중 하나라도 예외를 처리하면 예외는 더 이상 상위 블록으로 전파되지 않는다. 에외 처리기의 실행이 완료되면 예외 처리를 수행한 블록의 바로 다음에 위치한 문장으로 실행위치가 이동되어 실행이 계속된다.
'DATABASE > SQL, PL-SQL' 카테고리의 다른 글
저장 서브프로그램 개요 (0) | 2021.03.03 |
---|---|
예외처리 - 2 (0) | 2021.02.25 |
동적 SQL (0) | 2021.02.20 |
Cursor(커서) - 2 (0) | 2021.02.18 |
Cursor(커서) - 1 (0) | 2021.02.17 |
댓글