시스템 예외
예외처리시 OTHERS 외에 사용할 수 있는 시스템 예외명이 존재하는데, 미리 정의된 예외라고 한다.
종류
예제
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | CREATE OR REPLACE PROCEDURE EXCEPTION_PROC IS VI_NUM NUMBER := 0; BEGIN VI_NUM := 10 / 0; DBMS_OUTPUT.PUT_LINE('SUCCESS'); EXCEPTION WHEN ZERO_DIVIDE THEN DBMS_OUTPUT.PUT_LINE('오류1'); DBMS_OUTPUT.PUT_LINE(SQLERRM); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('오류2'); DBMS_OUTPUT.PUT_LINE(SQLERRM); END; EXEC EXCEPTION_PROC; | cs |
사원번호와 JOB_ID를 매개변수로 받아 해당 사원의 JOB_ID를 갱신하는데, JOB_ID가 JOBS 테이블에 존재하지 않으면 오류 메시지를 출력하고 종료하는 프로시저를 만들어보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | CREATE OR REPLACE PROCEDURE UPD_JOBID_PROC ( P_EMPLOYEE_ID EMPLOYEES.EMPLOYEE_ID%TYPE, P_JOB_ID JOBS.JOB_ID%TYPE ) IS VN_CNT NUMBER := 0; BEGIN SELECT COUNT(*) INTO VN_CNT FROM JOBS WHERE JOB_ID = P_JOB_ID; IF VN_CNT = 0 THEN DBMS_OUTPUT.PUT_LINE('JOB_ID가 없습니다.'); ELSE UPDATE EMPLOYEES SET JOB_ID = P_JOB_ID WHERE EMPLOYEE_ID = P_EMPLOYEE_ID; END IF; COMMIT; END; EXEC UPD_JOBID_PROC(200, 'SM_JOB2'); | cs |
IF문이 너무 번거로우므로, IF문 없이 다시 작성해보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | CREATE OR REPLACE PROCEDURE UPD_JOBID_PROC ( P_EMPLOYEE_ID EMPLOYEES.EMPLOYEE_ID%TYPE, P_JOB_ID JOBS.JOB_ID%TYPE ) IS VN_CNT NUMBER := 0; BEGIN SELECT 1 INTO VN_CNT FROM JOBS WHERE JOB_ID = P_JOB_ID; UPDATE EMPLOYEES SET JOB_ID = P_JOB_ID WHERE EMPLOYEE_ID = P_EMPLOYEE_ID; COMMIT; EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE(SQLERRM); DBMS_OUTPUT.PUT_LINE(P_JOB_ID || '에 해당하는 JOB_ID가 없습니다.'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('기타에러 : ' || SQLERRM); END; EXEC UPD_JOBID_PROC(200, 'SM_JOB2'); | cs |
예외처리부가 생겨 코드길이는 한 두줄 길어졌지만, 깔끔하기도 하고 의도가 분명한 코드가 된 것 같다.
사용자 정의 예외
사용자가 직접 예외를 정의하고 처리할 로직을 작성한다.
방법
1. 예외 정의
1 | 사용자정의 예외명 EXCEPTION; | cs |
사용자 예외를 사용하려면 일단 변수나 상수처럼 선언부에 예외를 정의해야한다.
2. 예외 발생시키기
1 | RAISE 사용자정의 예외명; | cs |
시스템 예외는 해당 예외가 자동으로 검출되지만, 사용자 정의 예외는 직접 예외를 발생시켜야한다.
3. 발생된 예외 처리
1 | EXCEPTION WHEN 사용자정의 예외명 THEN | cs |
예외를 발생시키면 자동으로 제어권이 EXCEPTION으로 넘어오므로 시스템 예외와 동일한 방식으로 처리하면된다.
예제
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | CREATE OR REPLACE PROCEDURE INS_EMP_PROC ( P_EMP_NAME EMPLOYEES.EMP_NAME%TYPE, P_DEPARTMENT_ID DEPARTMENTS.DEPARTMENT_ID%TYPE ) IS VN_EMPLOYEE_ID EMPLOYEES.EMPLOYEE_ID%TYPE; VD_CURR_DATE DATE := SYSDATE; VN_CNT NUMBER := 0; EX_INVALID_DEPID EXCEPTION; BEGIN SELECT COUNT(*) INTO VN_CNT FROM DEPARTMENTS WHERE DEPARTMENT_ID = P_DEPARTMENT_ID; IF VN_CNT = 0 THEN RAISE EX_INVALID_DEPID; END IF; SELECT MAX(EMPLOYEE_ID) + 1 INTO VN_EMPLOYEE_ID FROM EMPLOYEES; INSERT INTO EMPLOYEES (EMPLOYEE_ID, EMP_NAME, HIRE_DATE, DEPARTMENT_ID) VALUES (VN_EMPLOYEE_ID, P_EMP_NAME, VD_CURR_DATE, P_DEPARTMENT_ID); COMMIT; EXCEPTION WHEN EX_INVALID_DEPID THEN DBMS_OUTPUT.PUT_LINE('해당 부서 번호가 없습니다.'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLERRM); END; EXEC INS_EMP_PROC('홍길동', 999); | cs |
원래 부서 테이블에 입력받은 부서ID가 존재하지 않는것은 예외가 아니다. 데이터가 없는게 예외는 아니니깐.
하지만 위 코드에서는 사용자가 입력받은 부서ID가 존재하지 않는것을 예외로 정했기 때문에 예외처리부로 제어권이 넘어온 것이다.
시스템 예외에 이름 부여
예외명이 부여된 시스템 예외는 극소수이기 대문에 나머지는 예외코드로만 존재한다. 하지만 이런 이름없는 예외에 이름을 붙이면 좀 더 편하고 쉽게 사용할 수 있을것이다.
순서
1. 사용자정의 예외 선언
1 | 사용자정의 예외명 EXCEPTION; | cs |
2. 사용자정의 예외명과 시스템 예외코드 연결
1 | PRAGMA EXCEPTION_INIT (사용자정의예외명, 시스템예외코드); | cs |
3. 발생된 예외 처리
예제
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | CREATE OR REPLACE PROCEDURE INS_EMP_PROC ( P_EMP_NAME EMPLOYEES.EMP_NAME%TYPE, P_DEPARTMENT_ID DEPARTMENTS.DEPARTMENT_ID%TYPE, P_HIRE_MONTH VARCHAR2 ) IS VN_EMPLOYEE_ID EMPLOYEES.EMPLOYEE_ID%TYPE; VD_CURR_DATE DATE := SYSDATE; VN_CNT NUMBER:= 0; EX_INVALID_DEPID EXCEPTION; EX_INVALID_MONTH EXCEPTION; PRAGMA EXCEPTION_INIT (EX_INVALID_MONTH, -1843); BEGIN SELECT COUNT(*) INTO VN_CNT FROM DEPARTMENTS WHERE DEPARTMENT_ID = P_DEPARTMENT_ID; IF VN_CNT = 0 THEN RAISE EX_INVALID_DEPID; END IF; IF SUBSTR(P_HIRE_MONTH, 5, 2) NOT BETWEEN '01' AND '12' THEN RAISE EX_INVALID_MONTH; END IF; SELECT MAX(EMPLOYEE_ID) + 1 INTO VN_EMPLOYEE_ID FROM EMPLOYEES; INSERT INTO EMPLOYEES (EMPLOYEE_ID, EMP_NAME, HIRE_DATE, DEPARTMENT_ID) VALUES (VN_EMPLOYEE_ID, P_EMP_NAME, TO_DATE(P_HIRE_MONTH || '01'), P_DEPARTMENT_ID); COMMIT; EXCEPTION WHEN EX_INVALID_DEPID THEN DBMS_OUTPUT.PUT_LINE('해당 부서번호가 없습니다'); WHEN EX_INVALID_MONTH THEN DBMS_OUTPUT.PUT_LINE(SQLCODE); DBMS_OUTPUT.PUT_LINE(SQLERRM); DBMS_OUTPUT.PUT_LINE('1~12월 범위를 벗어났습니다'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLERRM); END; EXEC INS_EMP_PROC('홍길동', 110, '201314'); | cs |
주의할 점
미리 정의된 예외 중 NO_DATA_FOUND(-1403)은 사용자 정의 예외명에 해당 코드를 연결할 수 없다. 만약 연결할 경우 컴파일시 다움과 같은 오류가 발생한다.
예) PRAGMA EXCEPTION_INIT에 대한 부당한 ORACLE 오류번호(-1403)입니다.
예외 코드로 0이나 100을 제외한 양수, 그리고 -10000000 이하 값은 사용할 수 없다.
동일한 예외명으로 다른 예외 코드를 2개이상 연결하면, 맨 마지막에 연결한 코드가 적용된다.
RAISE와 RAISE_APPLICATION_ERROR
사용자 정의 예외명을 선언하고 미리 정의된 예외코드를 연결하거나 사용자 정의 예외를 선언하지않고 바로 미리 정의된 예외를 발생시킬 수 있다.
예제
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | CREATE OR REPLACE PROCEDURE RAISE_TEST_PROC ( P_NUM NUMBER ) IS BEGIN IF P_NUM <= 0 THEN RAISE INVALID_NUMBER; END IF; DBMS_OUTPUT.PUT_LINE(P_NUM); EXCEPTION WHEN INVALID_NUMBER THEN DBMS_OUTPUT.PUT_LINE('양수만 입력받을 수 있습니다.'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLERRM); END; EXEC RAISE_TEST_PROC(-10); | cs |
RAISE_APPLICATION_ERROR
시스템 패키지에 속한 프로시저이다. 사용자 정의 예외만 발생시킬 수 있고, 원하는 예외 코드와 메시지를 직접 매개변수로 받는다.
1 | RAISE_APPLICATION_ERROR(예외코드, 예외메시지); | cs |
예제
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | CREATE OR REPLACE PROCEDURE RAISE_TEST_PROC ( P_NUM NUMBER ) IS BEGIN IF P_NUM <= 0 THEN RAISE_APPLICATION_ERROR(-20000, '양수만 입력받을 수 있습니다.'); END IF; DBMS_OUTPUT.PUT_LINE(P_NUM); EXCEPTION WHEN INVALID_NUMBER THEN DBMS_OUTPUT.PUT_LINE('양수만 입력받을 수 있습니다.'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE); DBMS_OUTPUT.PUT_LINE(SQLERRM); END; EXEC RAISE_TEST_PROC(-10); | cs |
'📃 DATABASE > ORACLE' 카테고리의 다른 글
[PL/SQL] 트랜잭션 (0) | 2018.01.29 |
---|---|
[PL/SQL] 예외처리(3) : 효율적인 예외처리 (2) | 2018.01.26 |
[PL/SQL] 예외처리 (1) : 예외처리란? (0) | 2018.01.24 |
[PL/SQL] 프로시저(PROCEDURE) (0) | 2018.01.23 |
[PL/SQL] 사용자 정의함수 (0) | 2018.01.22 |