ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [PL/SQL] 예외처리
    IT, 프로그래밍/Database (Oracle) 2019. 1. 17. 08:30


    간단하게 설명하자면, 컴파일 에러는 컴파일러가 컴파일시에 확인하여 나오는 오타같은 에러고,

    예외는 컴파일이 되어 프로그램이 작동되는 중이지만, 예기치 못한 null값의 입력등으로 생기는 예외이다.

    시스템 예외는 오라클이 미리 이런 예외 상황에 대해 미리 정의해 놓은 예외이며, 오라클 시스템에서 자동으로 잡아준다.

    사용자 정의 예외는 예상되는 특정 예외 상황에 대해 개발자가 직접 정의한 예외이다


    EXCEPTION WHEN 예외명1 THEN 예외처리 구문1

    WHEN 예외명2 THEN 예외처리 구문2

    ...

    WHEN OTHERS THEN 예외처리 구문n; 



    자바 프로그래밍을 해 본 사람이라면, try-catch 문과 유사하다는 점을 떠올릴 수 있을 것이다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
            try {
                FileInputStream in = new FileInputStream(new File("C:/text.txt"));
                BufferedInputStream buffer = new BufferedInputStream(in);
                //... 이하 생략
            } catch (IOException e) {
                e.printStackTrace();
            } catch (NullPointerException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
    cs


    이런 식으로 먼저 처리할 예외부터 차례로 처리하면서, 필요한 예외 처리 로직을 작성한 후에

    마지막에는 예외들의 최상위 부모인 Exception으로 선언하여, 동적 바인딩을 통해 다양한 예외를 한 번에 처리한다.

    (모든 예외는 Exception 클래스를 상속받고 있어서 가능하다.) 


    PL/SQL에서의 예외처리도 먼저 처리할 예외를 위에 정의하여 적절히 처리한 후에,

    마지막에 한 번에 처리할 예외들은 OTHERS로 선언하여 처리한다.

    OTHERS는 반드시 맨 끝에 정의하여야 한다.



    1
    2
    3
    4
    5
    6
    7
    8
    DECLARE
       vi_num NUMBER := 0;
    BEGIN
       vi_num := 10/0;
       DBMS_OUTPUT.PUT_LINE('성공!');
    EXCEPTION WHEN OTHERS THEN
       DBMS_OUTPUT.PUT_LINE('오류가 발생하였습니다!');
    END;
    cs



    간단한 익명블록으로 예외를 테스트 해 보자.


    정수를 0으로 나눌 수 없으니 시스템 정의 예외가 발생한다.






    해당 예외가 발생했을 때, 미리 정의해 뒀던 로직을 수행 후에 그 뒤에 있는 구문이 정상적으로 작동이 가능하다.

    예외를 적당히 처리하지 않으면 그 시점에서 시스템이 죽어버린다.


    1
    2
    3
    4
    5
    6
    7
    8
    CREATE OR REPLACE PROCEDURE exception_test_proc
    IS
       vi_num NUMBER := 0;
    BEGIN
       vi_num := 10/0;
    EXCEPTION WHEN OTHERS THEN
       DBMS_OUTPUT.PUT_LINE('오류가 발생하였습니다!');
    END;
    cs



    위의 예외 예제를 프로시저로 작성하자.


    만약 EXCEPTION WHEN OTHERS... 구문이 없으면 10을 0으로 나누려고 시도하는 시점에서 뒤에 어떤 로직이 있든간에 상관없이 거기서 시스템이 멈춰버린다.


    위의 프로시저에서는 예외를 적절히 처리하였으므로 예외가 발생시 오류가 발생하였다는 메시지를 띄우고 그 뒤에 있는 로직을 수행할 것이다.


    1
    2
    3
    4
    5
    DECLARE
    BEGIN
      exception_test_proc();
      dbms_output.put_line('예외가 발생한 곳을 지났음.. ');
    END;
    cs


    익명 블록을 통해서 테스트 해 보면 오류가 발생하였다는 구문에 이어서 예외가 발생한 곳이 지났다는 메시지도 같이 뜨는데 이는 예외처리를 한 곳은 그 뒤의 로직을 정상적으로 처리할 수 있음을 보여준다.





    * 예외 메시지 확인


    자바에서는 예외처리시 printStackTrace() 등의 메소드를 통해 발생한 예외의 정보를 확인할 수 있도록 지원한다.


    PL/SQL 에서는 SQLCODE, SQLERRM을 통해서 발생한 예외의 정보를 확인할 수 있다. 


    이 기능은 오라클에서 기본적으로 제공 해 주는 빌트인(built-in) 함수이다.



    SQLCODE : 실행부에서 발생한 예외에 해당하는 코드를 반환한다.

    SQLERRM : 예외에 대한 정보를 담은 메시지를 반환한다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    CREATE OR REPLACE PROCEDURE exception_test_proc
    IS
       vi_num NUMBER := 0;
    BEGIN
       vi_num := 10/0;
    EXCEPTION WHEN OTHERS THEN
       DBMS_OUTPUT.PUT_LINE('오류가 발생하였습니다!');
       DBMS_OUTPUT.PUT_LINE('에러코드 : ' || SQLCODE);
       DBMS_OUTPUT.PUT_LINE('에러메시지 : ' || SQLERRM);
       DBMS_OUTPUT.PUT_LINE('매개변수 있는 에러메시지 : ' || SQLERRM(SQLCODE));
    END;
    cs


    아까 사용했던 exception_test 프로시저를 재정의 하여 테스트 해 보자.

    아까와 같이 호출하면,


    오류가 발생하였습니다!

    에러코드 : -1476

    에러메시지 : ORA-01476: divisor is equal to zero

    매개변수 있는 에러메시지 : ORA-01476: divisor is equal to zero

    예외가 발생한 곳을 지났음.. 



    이런 메시지를 확인할 수 있을 것이다.


    만약 DBMS_OUTPUT.PUT_LINE('매개변수 있는 에러메시지 : ' || SQLERRM(-1478));  이라는 다른 코드를 매개변수로 전달 해 보면 어떨까?


    매개변수 있는 에러메시지 : ORA-01478: array bind may not include any LONG columns


    발생한 에러와 전혀 다른 메시지를 전해준다.

    이렇듯 에러코드를 직접 전달하여 에러정보를 보여줄 수도 있다.



    그 외에도


    • dbms_utility.format_call_stack
    • dbms_utility.format_error_stack
    • dbms_utility.format_error_backtrace


    이라는 함수를 사용하면 더 상세한 정보를 얻을 수 있다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    CREATE OR REPLACE PROCEDURE exception_test_proc
    IS
       vi_num NUMBER := 0;
    BEGIN
       vi_num := 10/0;
    EXCEPTION WHEN OTHERS THEN
       DBMS_OUTPUT.PUT_LINE('오류가 발생하였습니다!');
       DBMS_OUTPUT.PUT_LINE('에러코드 : ' || SQLCODE);
       DBMS_OUTPUT.PUT_LINE('에러메시지 : ' || SQLERRM);
       DBMS_OUTPUT.PUT_LINE('상세 에러메시지 : ' || SYS.dbms_utility.format_call_stack);
       DBMS_OUTPUT.PUT_LINE('상세 에러메시지 : ' || SYS.dbms_utility.format_error_stack);
       DBMS_OUTPUT.PUT_LINE('상세 에러메시지 : ' || SYS.dbms_utility.format_error_backtrace);
     
    END;
    cs


    오류가 발생하였습니다!

    에러코드 : -1476

    에러메시지 : ORA-01476: divisor is equal to zero

    상세 에러메시지 : ----- PL/SQL Call Stack -----

      object      line  object

      handle    number  name

    00007FFB61A54A68        10  procedure USER1234.EXCEPTION_TEST_PROC

    00007FFB61A17BB8         3  anonymous block


    상세 에러메시지 : ORA-01476: divisor is equal to zero


    상세 에러메시지 : ORA-06512: at "USER1234.EXCEPTION_TEST_PROC", line 5


    예외가 발생한 곳을 지났음.. 




    시스템 예외의 종류




    아래는 오라클에서 대표적으로 지원하는 시스템 예외들이다.


    예외명예외 코드설명
    ACCESS_INTO_NULLORA-06530LOB과 같은 객체 초기화 되지 않은 상태에서 사용
    CASE_NOT_FOUNDORA-06592CASE문 사용시 구문 오류
    CURSOR_ALREADY_OPENORA-06511커서가 이미 OPEN된 상태인데 OPEN 하려고 시도
    DUP_VAL_ON_INDEXORA-00001유일 인덱스가 있는 컬럼에 중복값으로 INSERT, UPDATE 수행
    INVALID_CURSORORA-01001존재하지 않는 커서를 참조
    INVALID_NUMBERORA-01722문자를 숫자로 변환할 때 실패할 경우
    LOGIN_DENIEDORA-01017잘못된 사용자 이름이나 비밀번호로 로그인을 시도
    NO_DATA_FOUNDORA-01403SELECT INTO 시 데이터가 한 건도 없을 경우
    NOT_LOGGED_ONORA-01012로그온되지 않았는데 DB를 참조할 때
    PROGRAM_ERRORORA-06501PL/SQL 코드상에서 내부 오류를 만났을 때, 이 오류가 발생하면 “오라클에 문의(Contact Oracle Support)”란 메시지가 출력됨
    STORAGE_ERRORORA-06500프로그램 수행 시 메모리가 부족할 경우
    TIMEOUT_ON_RESOURCEORA-00051데이터베이스 자원을 기다리는 동안 타임아웃 발생 시
    TOO_MANY_ROWSORA-01422SELECT INTO 절 사용할 때 결과가 한 로우 이상일 때
    VALUE_ERRORORA-06502수치 또는 값 오류
    ZERO_DIVIDEORA-014760으로 나눌 때






    사용자 정의 예외




    사용자 정의 예외란, 개발자가 직접 예외를 정의해서 사용할 때의 예외이다. 


    시스템상 정상적으로 작동함에도, 프로그램의 의도한 것과는 전혀 다른 입력을 받았을 때 이런 예외를 정의하여 사용할 수 있다.


    예를 들어, 어떤 프로그램은 반드시 키가 150~170cm 사이의 사람의 정보를 입력받아야 하는데 어떤 사용자가 실수로 180cm임에도 불구하고 값을 넣었다고 치자. 그런 상황은 시스템 예외에는 정의되지 않았으므로 프로그램은 정상 작동 할 것이다.


    그러나 그 프로그램의 입장으로써는 들어와서는 안될 데이터가 들어온 것이니, 당장 그 입력된 데이터를 롤백 시키고 ' 당신은 들어오면 안되는 사람입니다! ' 라는 메시지를 띄워줘야 할 것이다.


    이런 것을 정의한 예외가 사용자 정의 예외이다.


    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 exception_test_proc2( vs_emp_number employees.phone_number%TYPE )
    IS
        vn_cnt NUMBER := 0;
        ex_not_exist_phone EXCEPTION;
     
    BEGIN
     
        SELECT COUNT(*)
        INTO vn_cnt
        FROM employees
        WHERE vs_emp_number = phone_number;
     
    IF vn_cnt > THEN
        DBMS_OUTPUT.PUT_LINE('등록된 사원의 전화번호 입니다.');
    ELSE 
        RAISE ex_not_exist_phone;
    END IF;
     
    EXCEPTION
    WHEN ex_not_exist_phone THEN
        DBMS_OUTPUT.PUT_LINE('등록된 번호가 없습니다.');
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE(SQLERRM);
     
    END;
    cs




    이 예제는 전화번호를 입력받아 사원 테이블에 존재하는 번호인지 아닌지 확인하는 예제이다.


    만약 존재하지 않는 번호라면, 이 번호는 유효한 번호가 아니므로 예외를 띄워서 처리한다.


    먼저 존재하는 사원의 번호를 매개변수로 넘겨줬을 경우의 결과이다.


    PL/SQL 프로시저가 성공적으로 완료되었습니다.


    등록된 사원의 전화번호 입니다.



    존재하지 않는 사원의 번호를 매개변수를 넘겨줬을 때 미리 정의해 둔 예외로 처리된다.



    PL/SQL 프로시저가 성공적으로 완료되었습니다.


    등록된 번호가 없습니다.





    * RAISE문


    시스템 예외나 사용자 정의 예외를 강제적으로 발생 시킬 수 있다.

    자바 프로그래밍에서 throw 키워드와 비슷하다. 자바에서도  throw new NullPointerException(); 같은 식으로 예외를 발생 시킬 수 있는데 이와 같은 개념이다. 



    시스템 예외 발생


    1
    2
    3
    4
    5
    6
    DECLARE
    BEGIN
      RAISE ACCESS_INTO_NULL;
      
      EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLERRM); 
    END;
    cs

    간단하게 RAISE와 시스템 예외명을 적는 것으로 예외를 발생 시킬 수 있다.


    PL/SQL 프로시저가 성공적으로 완료되었습니다.


    ORA-06530: Reference to uninitialized composite




    사용자 정의 예외와 시스템 예외의 연결





    만약 시스템 예외에 예외명이 부여되지 않은 경우 아래와 같은 방법으로 예외명을 부여할 수 있다.

    (예외명이 미리 정의된 것은 극소수이며 예외 코드만 존재하는 경우가 많다)


    이것들을 모두 보려면 오라클 사이트를 참조하자.


    친절하게 링크를 걸어둠 => >>요기요<<



    1. 사용자 정의 예외선언 : 사용자_정의_예외명 EXCEPTION;

    위의 예시에서 본 것과 같이 선언부에 예외명을 선언한다.


    2. 사용자 정의 예외명과 시스템 예외 코드의 연결 => 

    PRAGMA EXCEPTION_INIT (사용자_정의_예외명, 시스템_예외_코드)


    3. 발생된 예외의 처리 : EXCEPTION WHEN 사용자_정의_예외명 THEN..... 




    어떻게 돌아가는지 확인하기 위해 연관성은 없지만 01802 에러코드를 가진 예외와 연결시킬 것이다.


    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
     
    CREATE OR REPLACE PROCEDURE exception_test_proc2( vs_emp_number employees.phone_number%TYPE )
    IS
        vn_cnt NUMBER := 0;
        ex_not_exist_phone EXCEPTION;
        PRAGMA EXCEPTION_INIT (ex_not_exist_phone, -1802);
     
    BEGIN
     
        SELECT COUNT(*)
        INTO vn_cnt
        FROM employees
        WHERE vs_emp_number = phone_number;
     
    IF vn_cnt > THEN
        DBMS_OUTPUT.PUT_LINE('등록된 사원의 전화번호 입니다.');
    ELSE 
        RAISE ex_not_exist_phone;
    END IF;
     
    EXCEPTION
    WHEN ex_not_exist_phone THEN
        DBMS_OUTPUT.PUT_LINE('등록된 번호가 없습니다.');
        DBMS_OUTPUT.PUT_LINE(SQLERRM);
        DBMS_OUTPUT.PUT_LINE(SQLCODE);
     
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE(SQLERRM);
     
    END;
     
    cs



    중요한 것은 EXCEPTION_INIT 구문인데, 여기서 사용자 정의 예외와 시스템 예외를 연결시킬 수 있다.



    PL/SQL 프로시저가 성공적으로 완료되었습니다.


    등록된 번호가 없습니다.

    ORA-01802: Julian date is out of range

    -1802





    * 중요한 점

    1. 미리 정의된 예외 중 NO_DATA_FOUND(-1403)은 사용자 정의 예외명에 해당 코드(-1403)을 연결할 수 없다. 연결할 시에 부당한 ORACLE오류번호라고 빼액거릴것이다.

    2. 예외 코드로는 양수는 0 혹은 100, 음수는 -10,000,000 이상 까지만 사용이 가능하다.

    3. 동일한 예외명으로 다른 예외코드를 2개 이상 연결하면, 맨 마지막에 연결한 코드가 적용됨.




    예외코드와 예외 메시지 직접 초기화 하기




    RAISE_APPLICATION_ERROR (예외코드, 예외 메시지)


    예외코드 -20000 ~ -20999번 까지 범위 내에, 예외 코드와 예외 메시지를 사용자가 직접 정의할 수 있다.



    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    CREATE OR REPLACE PROCEDURE exception_test_proc2( vs_emp_number employees.phone_number%TYPE )
    IS
        vn_cnt NUMBER := 0;
     
    BEGIN
     
        SELECT COUNT(*)
        INTO vn_cnt
        FROM employees
        WHERE vs_emp_number = phone_number;
     
    IF vn_cnt > THEN
        DBMS_OUTPUT.PUT_LINE('등록된 사원의 전화번호 입니다.');
    ELSE 
        RAISE_APPLICATION_ERROR(-20200'등록된 번호가 없습니다. 에러코드 20200');
    END IF;
     
    EXCEPTION WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLCODE);
      DBMS_OUTPUT.PUT_LINE(SQLERRM);
     
    END;
    cs



    예외 발생시, 정의한 에러코드와 메시지가 나타난다.


    PL/SQL 프로시저가 성공적으로 완료되었습니다.


    -20200

    ORA-20200: 등록된 번호가 없습니다. 에러코드 20200





    효율적인 예외 처리 방법은 아래와 같은 방식이 있다.


    1. 시스템 예외인 경우는 OTHERS를 사용

    2. 예외 처리 루틴을 공통 모듈화하고, 발생된 예외 로그를 남기기
    (로그 테이블을 만들어 예외 발생시 기록하라!)

    3. 사용자 정의 예외를 별도 테이블로 만들어 관리하라


    'IT, 프로그래밍 > Database (Oracle)' 카테고리의 다른 글

    [PL/SQL] 커서 (Cursor)  (0) 2019.01.17
    [PL/SQL] 트랜잭션 (Transaction)  (0) 2019.01.17
    [PL/SQL] 함수와 프로시저  (4) 2019.01.16
    [PL/SQL] 기초 문법  (0) 2019.01.16
    [PL/SQL] PL/SQL 기초  (0) 2019.01.15
Designed by Tistory.