본문 바로가기
DATABASE/ORACLE

[PL/SQL] 무작정 시작하기 (5) - EXCEPTION 예외

by 허도치 2020. 1. 16.

2020/01/13 - [DATABASE/ORACLE] - [PL/SQL] 무작정 시작하기 (1) - PL/SQL 이란?

2020/01/13 - [DATABASE/ORACLE] - [PL/SQL] 무작정 시작하기 (2) - IF 조건문

2020/01/13 - [DATABASE/ORACLE] - [PL/SQL] 무작성 시작하기 (3) - LOOP, WHILE 반복문

2020/01/14 - [DATABASE/ORACLE] - [PL/SQL] 무작정 시작하기 (4) - CURSOR

 

 

  지난 포스트에서는 PL/SQL에서 가장 많이 사용되는 CURSOR에 대해서 다루어 보았다. CURSOR와 더불어 PL/SQL에서 가장 중요한 요소라고 생각하는 것은 바로 예외(Exception)처리이다. 왜냐하면, 대부분의 프로그램에서 예외가 발생하는 경우 중 가장 많은 비율을 차지하는 것이 바로 '예상되지 않은 데이터'가 유입된다는 것인데, 데이터를 직접적으로 조작하는 PL/SQL에서는 당연히 필수적인 요소라고 생각하기 때문이다. 그래서 이번 포스트에서는 예외처리(Exception)에 대해서 다루어보려고 한다.

 

 

1. 예외(Exception)처리란?

   1-1. 프로그램이 수행하던 중 예상하지 못한 오류가 발생했을 때의 대처 방안.

 

   1-2. 오류가 발생했을 때, 오류 종류에 따라 제어할 수 있음.

         - 크리컬한 오류인 경우 프로그램을 종료.

         - 시스템의 영향도가 작은 오류라면 무시하고 계속 진행.

 

   1-3. PL/SQL에서는 주로 로그를 남기거나 TCL(트렌젝션 제어어, COMMIT, ROLLBACK )을 처리함.

 

   1-4. 사용자 임의의 EXCEPTION을 정의할 수 있음.

         - 단, 사용자 정의 EXCEPTIONSQLCODE는 '1', SQLERRM은 'User-Defined Exception'으로 출력됨.

         - 따라서, 메시지가 필요한 경우 별도의 과정이 필요함.

 

   1-5. 단, 순수 SQL로 작성된 쿼리에서 발생한 오류는 PL/SQL에서 EXCEPTION으로 캐치할 수 없음.

 

 

2. 기본 구조.

   2-1. BEGIN과 END 사이에 위치하며, CASE WHEN문과 같이 분기처리할 수 있음.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
DECLARE
 
BEGIN
 
EXCEPTION
    WHEN SOME_EXCEPTION THEN
        COMMIT;
            
 
    WHEN OTHERS THEN
        V_RESULT_MSG := 'SQLCODE['||SQLCODE||'], MESSAGE =>'||SQLERRM;
        
        DBMS_OUTPUT.PUT_LINE( V_RESULT_MSG );
        
        ROLLBACK;
 
END;
cs

         - 6 ln: SOME_EXCEPTION은 사용자가 정의한 EXCEPTION을 의미함.

         - 7 ln: EXCEPTION이 발생했을 경우 COMMIT을 하도록 함.

         - 10 ln: 여기서 OTHERS는 상위 조건을 제외한 모든 EXCEPTION을 의미함.

         - 11~15 ln: SQLCODE, SQLERRM과 같이  내장 변수를 사용하여 로그를 출력할 수 있음.

 

 

3. 사용예제.

   3-1. 부서번호가 '30'인 사원에게 연봉의 10%를 보너스로 지급한다. 단, 대상이 10명 미만인 경우 취소하시오.

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
47
48
DECLARE
    
    V_INSERTCNT     NUMBER;
    MY_EXCEPTION    EXCEPTION;
 
BEGIN
 
    BEGIN
        /* 순수 SQL로 작성된 QUERY */
        INSERT INTO SCOTT.BONUS T1 ( ENAME, JOB, SAL, COMM )
        SELECT T2.ENAME
             , T2.JOB
             , T2.SAL+(T2.SAL*0.1) AS SAL
             , T2.COMM
          FROM SCOTT.EMP T2
         WHERE 1=1
           AND T2.DEPTNO = 30
        ;
        
        V_INSERTCNT := SQL%ROWCOUNT;
        
        IF V_INSERTCNT < 10 THEN
            RAISE MY_EXCEPTION;
        END IF;
        
        COMMIT;
        
        DBMS_OUTPUT.PUT_LINE( 'COMMIT -> '||V_INSERTCNT );
       
    EXCEPTION
        WHEN MY_EXCEPTION THEN 
            ROLLBACK;
            DBMS_OUTPUT.PUT_LINE( 'ROLLBACK -> '||V_INSERTCNT );
            DBMS_OUTPUT.PUT_LINE( 'MY_EXCEPTION -> '||SQLERRM );
            RAISE_APPLICATION_ERROR(-20001'보너스 지급 대상이 10명 미만입니다.');
            
        WHEN OTHERS THEN
            ROLLBACK;
            DBMS_OUTPUT.PUT_LINE( 'ROLLBACK -> '||V_INSERTCNT );
            DBMS_OUTPUT.PUT_LINE( 'INTERNAL OTHERS EXCEPTION -> '||SQLERRM );
    
    END;
 
EXCEPTION
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE( 'EXTERNAL OTHERS EXCEPTION -> '||SQLERRM );
 
END;
cs

         - 4 ln: 사용자 정의 Exception생성.

         - 20 ln: SQL%ROWCOUNT에는 가장 최근 처리된 SQL의 처리행의 수가 담겨있음.

           *SQL% 변수는 COMMIT, ROLLBACK이 되면 초기화됨.

         - 23 ln: 사용자 정의 Exception호출.

         - 31~35 ln: 사용자 정의 Exception을 캐치하여 정해진 로직을 수행.

           *SQLCODESQLERRM은 내장 변수

         - 35 ln: 사용자 정의 Exception에 코드와 메시지를 정의해주기위해 RAISE_APPLICATION_ERROR 함수 사용.

         - 37~40 ln: 나머지 모든 Exception을 캐치하여 정해진 로직을 수행.

         - 44~46 ln: 내부 Block에서 발생한 Exception을 캐치.

 

   3-2. RAISE_APPLICATION_ERROR 함수

         - 사용자 에러코드 및 에러메시지를 정의하고 Exception을 발생시키는 함수.

         - 이 방법은 현재 블록에서 Exception을 발생시켜 상위로 전파하므로 상위에서 Exception을 한번 더 캐치해야함.

         문법) RAISE_APPLICATION_ERROR( *SQLCODE, SQLERRM );

         사용) RAISE_APPLICATION_ERROR( -20001, '사용자 오류가 발생하였습니다.');

         *SQLCODE는 -20000에서 -29999 사이의 값으로 지정.

 

 

 

4. 마치며.

   - 3번 예제에 Exception을 다루는 방법을 최대한 많이 녹여내었다. 사실 저정도만 사용해도 크게 문제 없이 사용할 수 있다고 생각한다.

댓글