(이 문서는 Oracle University - Oracle Database 10g: PL/SQL FundamentalsO'Reilly - Oracle PL/SQL Programming 5th edition을 참조하여 작성 하였습니다.)

예외 처리란 무엇인가

예외 처리가 되지 않은 경우

first name이 Michael인 사람을 찾아서 그 사람의 last name을 출력하려고 한다. 이를 위해 위와 같이 쿼리를 작성했다면 이는 분명 문법적으로 맞는 문장이다. 따라서 사용자는 자신이 원한 결과를 정상적으로 얻을 수 있을 것이라 생각할 것이다. 하지만 막상 실행 결과를 보면 위와같이 에러가 발생하게 된다. 이 경우에 에러가 발생하게 된 이유는, last_name을 SELECT하여 lastname이라는 변수에 넣으려 했으나, first_name='Michael'이라는 조건을 달성하는 컬럼이 두 건이 넘었기에, 결과 값의 개수가 너무 많아 변수에 넣을 수 없어 에러가 발생한 것이다. 이와 같이 컴파일시(compile-time)에는 문제가 없으나, 실행 시점(run-time)에서 문제가 발생하는 경우 예외(Exceptions)가 발생했다고 한다. PL/SQL에서는 예외부라는 것을 두어 예외를 처리할 수 있으며, 이와같이 예외 처리가 되지 않은 상태에서 예외가 발생한다면, 위와 같이 해당 PL/SQL 블록은 비정상 종료된다. 

 

 

예외란?

바로 위의 예제에서 확인하였듯이, 런타임시에 발생되는 오류들을 예외라고 부른다. 크게 두가지의 종류가 있으며, 오라클이 발생시키는 예외와, 사용자가 직접 발생시키는 예외로 나뉘게 된다. 

PL/SQL의 블록 구조를 다시 확인해보시면 위와 같다. 여기서 '예외부'가 바로 예외 사항들을 처리해주는 부분이 된다. 실행부에서 예외가 발생하게되는 순간, 해당 실행부에서의 작업이 완전히 중단되게 되며, 발생된 예외 사항에 대해서 예외부로 전달해주게 된다. 예외부에서는 전달 받은 예외 사항에 해당하는 처리기(Handler)가 있는지 살펴보고, 처리기가 있다면 처리기에 기술된 내용대로 동작하게 되며, 이 경우 해당 예외가 처리된 것이다. 예외부의 구조는 아래와 같다.

EXCEPTION문으로 예외부가 시작되며, 구조는 CASE문과 비슷한 형태를 띄고 있다. WHEN exceptionN 구문에는 (사전 정의된, 혹은 선언부에서 선언한)예외의 이름을 지정해준다. OR문을 통해 하나의 처리기에서 하나 이상의 예외 사항을 처리할 수도 있다. statementN 부분에는 해당 처리기에서 실질적으로 처리해줄 내용을 넣어 줍니다. 만약 WHEN exceptionN을 통해 특정되지 못한 예외가 있다면 WHEN OTHERS 부분에 작성된 처리 내용을 통해 처리하게 된다. 

 

예외 처리가 되어 있는 경우

앞선 PL/SQL 구문에 이렇게 예외부만 추가하였다. 여전히 동일한 예외가 발생할 것이므로 사용자는 원하는 결과를 얻을 수는 없을 것이다. 하지만 위에서와 달리 예외부에 처리기가 생성되어 있으며, 해당 예외가 발생했을 때에 처리기에 의해 처리되게 된다. 따라서 해당 구문이 비정상 종료되지 않고 successfully하게 종료되었음을 확인할 수 있다. 또한 처리기에 의해서 Your select statement retrieved multiple rows. Consider using a cursor.라는 메세지가 출력된 것을 확인할 수 있다.

 

 

용어 정리

Execption sectionPL/SQL 블록 중 선택 적으로 사용되는 블록. 이 부분에는 하나 이상의 예외 처리기가 포함된다. CASE 구문과 구조가 유사함
= 예외부
RaisePL/SQL 런타임 엔진에 에러를 전달하여 실행 중인 PL/SQL 블록을 중단 시킨다. 데이터베이스 스스로도 에러를 발생시킬 수 있으며 RAISE, RAISE_APPLICATION_ERROR를 이용하여 사용자가 직접 에러를 발생시킬 수도 있다.
= 발생시키기
Handle, handler예외부에서 에러를 잡아내는(Trap) 것을 '처리한다'라고 한다. 예외부에서 잡힌 에러는 - 처리기에 의해 로그에 기록되거나, 유저에게 메세지를 띄우거나, 현재 블록 밖으로 전달되는 식으로 처리되도록 코딩할 수 있다.
= 처리하다, 처리기
Scope전체 코드 중에서 예외가 발생할 수 있는 부분을 뜻한다. 또한 발생한 예외를 잡아서 처리할 수 있는 부분이기도 하다.
= 예외 영역
Propagation해당 블록 내에서 예외가 처리되지 않는다면, 그 블록을 감싸고 있는 블록의 예외부로 예외를 전달해준다.
= 전달
Unhandled exception블록 내에서 예외가 처리되지 않는다면 전달 처리가 되는데, 이때에 가장 바깥쪽 블록까지 계속 전달되어 더이상 전달해줄 예외부가 없다면 해당 에러는 미처리된 예외가 된다. 미처리된 예외 사항은 호스트의 실행 환경으로 전달되고, 환경/프로그램이 예외를 처리하게 된다.
= 미처리된 예외
Un-named or anonymous exception에러 코드 번호와 설명은 부여되어 있지만, RAISE 구문에서 사용할 수 있는 이름이 없거나, 예외부에서 처리기가 인식할 수 있도록 WHEN 구문에 이름이 사용되지 않았다면 이름 없는 예외가 된다.
= 이름 없는 예외, 익명의 예외
Named exception오라클 내장 패키지나, 개발자 본인에 의해서 이름이 지정된 예외 사항을 이름 있는 예외라고 한다. EXCEPTION_INIT 프라그마를 이용해서 이 예외 사항에 이름을 연결할 수도 있고, 해당 이름에 의해서만 정의될 수 있도록 놔둘 수도 있다.
= 이름 있는 예외

 

예외 처리의 방법

에외가 발생되는 두가지 경우

  • 오라클에 의해 암시적으로 : 테이블이 존재하지 않는다거나, 메모리가 부족하다거나 하는 식의 에러 메세지, 즉, 예외 사항들은 오라클 DBMS 서버에 의해서 발생하게 된다. 또한 이렇게 오라클에 의해 발생되는 예외는 암시적인 예외라고 표현한다. 
  • 프로그램에 의해 명시적으로 : 프로그래머가 

예외를 처리하는 두가지 방법

  • 처리기로 가둔다. (trap) : 예외부에서 처리기(handler)를 통해 해당 예외가 발생했을 때에 어떠한 작업이 필요한지 작성해둔다. 이 때에 예외부에 처리되어있는 예외가 발생한다면 해당 처리기
  • 실행 환경으로 전달한다. (propagation) : 예외부에서 처리되지 못한 예외는, 해당 PL/SQL을 실행시킨 환경으로 전달되게 된다. 이는 말하자면 예외가 발생된 블록에서는 처리하지 않는다는 뜻이기도 하다. 만약 중첩 블록 구조로 작성되어 있다면 바깥 블록으로 예외가 전달되며, 가장 바깥쪽의 블록이었다면 사용자가 해당 PL/SQL 프로그램을 수행한 환경(이를테면, SQL*Plus. SQL Developer와 같은)으로 예외 사항을 전달하게 된다. 

 

예외의 종류

암시적

  • 오라클에 의해 사전 정의된 예외 : 자주 발생하는 에러들에 대해서는 오라클이 미리 이름을 붙여놓았다. 이러한 예외를 문자 그대로 오라클에 의해 사전 정의된 예외라고 한다.
  • 오라클에 의해 사전 정의되지 않은 예외 : 그 외의 에러들에 대해서는 오라클 에러 코드와 간단한 설명만이 제공되며, 이러한 예외 사항들을 처리하기 위해서는 사용자가 직접 해당 에러 코드에 이름을 붙여주어야 한다. 

명시적

  • 사용자 정의 예외 : 오라클이 발생시키는 에러가 아니라, RAISE, RAISE_APPLICATION_ERROR를 이용하여 사용자가 직접 발생시키는 에러를 의미한다. 

 

(예외를 발생시키는 주체는 둘(오라클,프로그램), 그에 따른 예외의 종류는 셋으로 나뉘며, 처리를 할수도 있고, 안할 수도 있음. 이때에 처리를 하는 방법은 예외의 종류에 따라서 세가지 방법으로 나뉘게 되며, 이에 대한 내용이 바로 아래쪽에 계속 설명된다.)

  

예외 처리하기

사전 정의된 예외 처리하기

오라클 PL/SQL은 자주 일어나는 몇가지 예외에 대해서 정의해 놓았으므로, 해당 예외 사항들에 대해서는 선언부에서 따로 선안할 필요도, 실행부에서 따로 예외를 발생 시킬 필요도 없다. 사전 정의 예외들은 STANDARD 패키지에 의해 정의되어 있으며 이 패키지는 PL/SQL Default 패키지에 속한다. 따라서 사전 정의된 이름을 사용할 때에 따로 패키지이름을 적어줄 필요도 없으며 그저 미리 정의되어 있는 이름을 예외부에서 사용하기만 하면 된다.

SQL> SET SERVEROUTPUT ON
SQL> DECLARE
  2  vempno NUMBER(4);
  3  vename VARCHAR(20);
  4  vsal NUMBER(10);
  5  BEGIN
  6  SELECT empno, ename, sal*12+NVL(comm, 0)
  7  INTO vempno, vename, vsal
  8  FROM emp
  9  WHERE empno=&vno;
 10  DBMS_OUTPUT.PUT_LINE('Employee# :'|| vempno );
 11  DBMS_OUTPUT.PUT_LINE('NAME :'|| vename);
 12  DBMS_OUTPUT.PUT_LINE('SALARY :'|| vsal);
 13  EXCEPTION
 14  WHEN NO_DATA_FOUND THEN
 15  DBMS_OUTPUT.PUT_LINE('Cannot find the employee');
 16  END;
 17  /
Enter VALUE FOR vno: 200
old   9: WHERE EMPNO=&VNO;
NEW   9: WHERE EMPNO=200;
Cannot find the employee

PL/SQL PROCEDURE successfully completed.

SQL>

 

 

사전 정의되어 있는 예외들은 아래와 같다. 모든 종류의 사전 정의 예외 목록을 확인해 보려면 PL/SQL User's Guide and Reference 문서를 확인해보면 될 것이다. 

예외 명오라클 서버 에러 코드설명
ACCESS_INTO_NULLORA-06530Attempted to assign values to the attributes of an uninitialized object
CASE_NOT_FOUNDORA-06592None of the choices in the WHEN clauses of a CASE statement are selected, and there is no ELSE clause.
COLLECTION_IS_NULLORA-06531Attempted to apply collection methods other than EXISTS to an uninitialized nested table or VARRAY
CURSOR_ALREADY_OPENORA-06511Attempted to open an already-open cursor
DUP_VAL_ON_INDEXORA-00001Attempted to insert a duplicate value
INVALID_CURSORORA-01001Illegal cursor operation occurred.
INVALID_NUMBERORA-01722Conversion of character string to number fails.
LOGIN_DENIEDORA-01017Logging on to the Oracle server with an invalid username or password
NO_DATA_FOUNDORA-01403Single row SELECT returned no data.
NOT_LOGGED_ONORA-01012PL/SQL program issues a database call without being connected to the Oracle server.
PROGRAM_ERRORORA-06501PL/SQL has an internal problem.
ROWTYPE_MISMATCHORA-06504Host cursor variable and PL/SQL cursor variable involved in an assignment have incompatible return types.
STORAGE_ERRORORA-06500PL/SQL ran out of memory, or memory is corrupted.
SUBSCRIPT_BEYOND_COUNTORA-06533Referenced a nested table or VARRAY element by using an index number larger than the number of elements in the collection
SUBSCRIPT_OUTSIDE_LIMITORA-06532Referenced a nested table or VARRAY element by using an index number that is outside the legal range (for example, –1)
SYS_INVALID_ROWIDORA-01410The conversion of a character string into a universal ROWID fails because the character string does not represent a valid ROWID.
TIMEOUT_ON_RESOURCEORA-00051Time-out occurred while the Oracle server was waiting for a resource.
TOO_MANY_ROWSORA-01422Single-row SELECT returned more than one row.
VALUE_ERRORORA-06502Arithmetic, conversion, truncation, or size-constraint error occurred.
ZERO_DIVIDEORA-01476Attempted to divide by zero

 

 

 

 

사전 정의되지 않은 예외 처리하기

SQL> SET SERVEROUTPUT ON
SQL> DECLARE
  2   insert_excep EXCEPTION;
  3   PRAGMA EXCEPTION_INIT
  4   (insert_excep, -01400);
  5  BEGIN
  6   INSERT INTO departments
  7   (department_name) VALUES (NULL);
  8  EXCEPTION
  9   WHEN insert_excep THEN
 10   DBMS_OUTPUT.PUT_LINE('INSERT OPERATION FAILED');
 11   DBMS_OUTPUT.PUT_LINE(SQLERRM);
 12  END;
 13  /
INSERT OPERATION FAILED
ORA-01400: cannot INSERT NULL INTO ("HR"."DEPARTMENTS"."DEPARTMENT_ID")

PL/SQL PROCEDURE successfully completed.

SQL>

 

  1. 예외의 이름을 선언한다. (선언부)
  2. PRAGMA EXCEPTION_INIT을 사용하여 해당 예외의 이름과 오라클 에러 코드 번호를 연결시킨다. (선언부)
  3. 해당 예외가 발생할 경우 처리할 수 있도록 처리기를 작성한다. (예외부)

 

 

사용자 정의 예외 처리하기

SQL> ACCEPT deptno PROMPT 'Please enter the department number:'
Please enter the department NUMBER:9821
SQL> ACCEPT name   PROMPT 'Please enter the department name:'
Please enter the department name:testing
SQL> DECLARE
  2    invalid_department EXCEPTION;
  3    name VARCHAR2(20):='&name';
  4    deptno NUMBER :=&deptno;
  5  BEGIN
  6    UPDATE  departments
  7    SET     department_name = name
  8    WHERE   department_id = deptno;
  9    IF SQL%NOTFOUND THEN
 10      RAISE invalid_department;
 11    END IF;
 12    COMMIT;
 13  EXCEPTION
 14    WHEN invalid_department  THEN
 15      DBMS_OUTPUT.PUT_LINE('No such department id.');
 16  END;
 17  /
old   3:   name VARCHAR2(20):='&name';
NEW   3:   name VARCHAR2(20):='testing';
old   4:   deptno NUMBER :=&deptno;
NEW   4:   deptno NUMBER :=9821;
No such department id.

PL/SQL PROCEDURE successfully completed.

 

  1. 예외의 이름을 선언한다. (선언부)
  2. RAISE, RAISE_APPLICATION_ERROR를 사용하여 사용자가 직접 예외를 발생시킨다. (실행부)
  3. 해당 예외가 발생할 경우 처리할 수 있도록 처리기를 작성한다. (예외부)

 

 

예외 트래핑 함수

에러가 발생 했을 때 아래의 두 함수를 통하여 해당 에러에 대한 오라클 에러 코드 번호와 간단한 설명 내용을 확인 가능하다. 

 

함수설명
SQLCODE오라클 에러 코드 번호를 반납한다.
SQLERRM해당 에러에 대한 간단한 설명 내용을 반납한다.

이 중에 SQLCODE의 경우 

SQL CODE 값설명
0예외가 발생하지 않은 경우
1사용자 정의 에러 코드가 발생한 경우
+100NO_DATA_FOUND 예외가 발생한 경우 SQLCODE가 100이된다.
음의 정수그 외의 오라클 서버 에러가 발생한 경우 해당 오라클 에러 넘버가 남는다.

이 두개의 함수는 아래와 같은 형태로 활용이 가능하다.

SQL> CREATE TABLE errors (
  2  e_user VARCHAR2(20),
  3  e_date DATE,
  4  error_code  NUMBER(20),
  5  error_message VARCHAR2(100)
  6  );

TABLE created.

SQL> DECLARE
  2    error_code      NUMBER;
  3    error_message   VARCHAR2(255);
  4    lastname VARCHAR2(20);
  5    insert_excep EXCEPTION;
  6    PRAGMA EXCEPTION_INIT
  7    (insert_excep, -01400);
  8  BEGIN  
  9    SELECT last_name INTO lastname FROM employees WHERE     
 10    first_name='Michael'; 
 11    DBMS_OUTPUT.PUT_LINE ('Michael''s last name is : '||lastname);
 12  EXCEPTION
 13    WHEN insert_excep THEN
 14     DBMS_OUTPUT.PUT_LINE('INSERT OPERATION FAILED');
 15     DBMS_OUTPUT.PUT_LINE(SQLERRM);
 16    WHEN OTHERS THEN
 17     ROLLBACK;
 18     error_code := SQLCODE;
 19     error_message := SQLERRM;
 20     INSERT INTO errors (e_user, e_date, error_code,error_message)
 21     VALUES(USER,SYSDATE,error_code,error_message);
 22  END;
 23  /

PL/SQL PROCEDURE successfully completed.

SQL> SELECT * FROM errors;

E_USER               E_DATE             ERROR_CODE ERROR_MESSAGE
-------------------- ------------------ ---------- ----------------------------------------------------------------------------------------------------
HR                   08-FEB-12               -1422 ORA-01422: exact FETCH returns more than requested NUMBER OF ROWS

SQL>

레이블 (0)

  • 레이블 없음

댓글  (0)

첨부 파일  (4)

첨부 파일 추가하기
  파일 변경됨
PNG 파일 Plsql002.png 7월 09, 2013 by 민항
PNG 파일 Plsql001.png 7월 09, 2013 by 민항
PNG 파일 Plsql003.png 7월 09, 2013 by 민항
PNG 파일 Plsql004.png 7월 09, 2013 by 민항