Database Interaction with PL/SQL: Pre-defined Exceptions

This is part 6 of a series focusing on database interactions with Oracle PL/SQL. The previous articles discussed several types of collections in PL/SQL. Now Jagadish will look into exception handling using the predefined exceptions available in Oracle PL/SQL.

Please note that all the examples in this series have been tested only with Oracle 10g and not with all the previous versions of Oracle. Refer to the documentation of the respective version you are using if any programs fail to execute.

Introduction to Exception Handling

Before going to exception handling, let us first determine all the possible ways to get errors. Errors may occur in PL/SQL (of course, not only PL/SQL) in several possible ways including hardware failures, network failures, bugs in programs, out of memory and several other reasons. We may not know which error has been raised at what moment. But we need to handle all such errors and provide some meaningful messages to the user (instead of making the user horrified with jargon-filled error messages).

An exception is generally a runtime error which gets raised by PL/SQL runtime when a PL/SQL block is in the process of execution. Handling the exception consists of trapping the error (or exception) and providing a meaningful explanation to the user without showing the weird error message raised by the PL/SQL run-time. This doesn’t mean only providing meaningful explanations; we can take certain actions (programmatically) when an error occurs at runtime.

Basically, there exist two types of exceptions:

  1. Predefined Exceptions

    Predefined exceptions are the ones which are already defined by Oracle team that correspond to the most common oracle errors (like division by zero, out of memory, etc.). We can handle them directly within our program without declaring them, and we have thousands of such type of exceptions. In this article, I cover the most important ones.

  2. User-Defined Exceptions

    User-Defined Exceptions must be declared and raised explicitly by the user (by issuing RAISE statements). These exceptions are created, used, raised and implemented by user himself. Oracle will not know about any of those exceptions (till it finds the declarations of those exceptions within the PL/SQL block). There exists EXCEPTION section within the PL/SQL block to handle any sort of exceptions and the section is optional. Let us start by looking into some of the most commonly used predefined exceptions.

{mospagebreak title=No Data Found, Too Many Rows}

NO_DATA_FOUND

NO_DATA_FOUND exception is raised when SELECT…INTO statement could not find any row (or value) to fetch into variables.

Let us consider the following example:

Declare
  v_empno emp.empno%Type := &empno;
  v_ename emp.ename%Type;
Begin
  Select ename into v_ename From emp 
  where empno=v_empno;
  dbms_output.put_line(‘Name: ‘||v_ename);
Exception
  when no_data_found then
    dbms_output.put_line(‘Employee not found’);
End;

From the above program, we declare two variables ‘v_empno’ and ‘v_ename’. We accept input from the user just by placing ‘&empno’. All this happens within declaration section. Within the body (between ‘begin’ and ‘end’), we fetch the ‘ename’ and place it in ‘v_ename’ from the table ’emp’, based on the ‘where’ condition framed from the user input. To display the value available in ‘v_ename’, we use DBMS_OUTPUT.PUT_LINE statement. If employee number could not be found, it will raise an exception where we need to handle it within the EXCEPTION section before ‘END’ statement.

The exception section is used to handle any type of exception. So, we need to specify the exception (which is to be handled) using WHEN statement within the exception section. Using DBMS_OUTPUT.PUT_LINE statement we can display the error ‘Employee not found’ on to the screen.

The flow of execution of program will be something like the following (in brief):

  • Declares the variables and accepts input from the user
  • Try to execute the SELECT statement and fetch ‘ename’ into ‘v_ename’
  • If SELECT statement gets successfully executed, it displays the employee name (next line of SELECT) and directly jumps to END (skipping the exception section).
  • If SELECT statement could not retrieve any row (in this case it is ‘ename’), it immediately jumps to exception section (skipping the DBMS_OUTPUT statement next to SELECT statement) and checks whether the exception (or error) is NO_DATA_FOUND or not. If so, displays the message ‘Employee not found’.

TOO_MANY_ROWS

Now that we understood about NO_DATA_FOUND, we shall look into TOO_MANY_ROWS. We all know very well that SELECT..INTO statement will not be able to retrieve more than one row. So, when a SELECT ….INTO statement in a PL/SQL blocks tries to return more than one row, TOO_MANY_ROWS exception gets raised. Let us consider the following program.

Declare
  v_emp emp%rowtype;
  v_Sal emp.sal%Type := &sal;
Begin
  Select * into v_emp From emp
  where sal=v_sal;
  dbms_output.put_line(‘Name:’||v_emp.ename);
Exception
  When Too_Many_rows then
    dbms_output.put_line(‘More than one employee having same salary’);
End ;

The above program is very similar to the first program, and the flow of execution of program will be something like the following (in brief):

  • Declares the variables and accepts input from the user
  • Try to execute the SELECT statement and fetch entire row from ’emp’ into ‘v_emp’
  • If SELECT statement gets successfully executed with only single row, it displays the employee name (next line of SELECT) and directly jumps to END (skipping the exception section).
  • If SELECT statement is trying to get more than one row (as there exists a possibility of having more than one employee with the same salary given), it immediately jumps to exception section (skipping the DBMS_OUTPUT statement next to SELECT statement) and checks whether the exception (or error) is TOO_MANY_ROWS or not. If so, displays the message ‘More than one employee having same salary’.

{mospagebreak title=Dup Val on Index, Zero Divide, Invalid Number, Value Error}

DUP_VAL_ON_INDEX

This is bit different from the above two exceptions. This exception is not at all related to SELECT..INTO statement (as above exceptions). This exception gets raised when you try to update (or even insert) a row with a duplicate value in to a column constrained by a unique index (obviously Primary key as well). Let us consider the following program.

Declare
  v_empno emp.empno%type:=&empno;
  v_ename emp.ename%type:=’&ename’;
  v_deptno emp.deptno%type:=&deptno;
Begin
  Insert into emp(empno,ename,deptno)
  Values (v_empno,v_ename,v_deptno);
  dbms_output.put_line(‘Employee Successfully Inserted’);
Exception
  When DUP_VAL_ON_INDEX then
    dbms_output.put_line(‘Employee ID already exists’);
End ;

The flow of execution of program will be something like the following:

  • Declares the variables and accepts input from the user
  • Tries to execute the INSERT statement based on the values provided by user
  • If INSERT statement gets successfully executed, it displays a positive message to user that the row has been inserted and directly jumps to END statement (skipping the exception section).
  • If INSERT fails due to duplication in a column, constrained with unique index (or primary key), it immediately jumps to exception section (skipping the DBMS_OUTPUT statement next to INSERT statement) and checks whether the exception (or error) is DUP_VAL_ON_INDEX or not. If so, displays the message ‘Employee ID already exists’.

ZERO_DIVIDE, INVALID_NUMBER, VALUE_ERROR

The above two predefined exceptions worked well with SELECT..INTO statement. But some predefined exceptions may not be related to SELECT..INTO statement. Let us see ZERO_DIVIDE, INVALID_NUMBER and VALUE_ERROR together within this section.

No number can be divided with zero as it results in infinite. To handle such type of situation, there exists ZERO_DIVIDE exception. Let us consider the following example.

Declare
  x number := &x;
  y number := &y;
  z number;
Begin
  z := x/y;
  dbms_output.put_line (‘Result: ‘ || z);
Exception
  When ZERO_DIVIDE then
    dbms_output.put_line (‘Division by zero occured’);
End ;

The above program illustrates simple variable declarations of numeric data type. Here, we declare three variables (x, y and z). The values of variables ‘x’ and ‘y’ are accepted from the user. Observe that all the variables are of type number (any numeric data type is acceptable). Within the body, we divide ‘x’ with ‘y’ and store the result in ‘z’.

Everything works fine as long as you provide proper values. If you try to provide the value of ‘y’ as zero, then ZERO_DIVIDE exception gets raised and a message gets displayed to user.

Coming to INVALID_NUMBER, I hope you can easily understand that whenever PL/SQL runtime is unable to convert a character string to number, the exception gets raised. The following example demonstrates the same.

Declare
  v_empno varchar2(4):=’&empno’;
  v_ename varchar2(20):=’&ename’;
  v_deptno varchar2(2):=’&deptno’;
Begin
  Insert into emp(empno,ename,deptno)
  Values (v_empno,v_ename,v_deptno);
Exception
  When INVALID_NUMBER then
     dbms_output.put_line(‘Given Employee Number or deptno is Invalid’);
End ;

Coming to VALUE_ERROR, this is almost similar to INVALID_NUMBER except that it works with almost any data type. INVALID_NUMBER gets raised only when SQL statement is issued whereas VALUE_ERROR gets raised with PL/SQL statements. Let us go through the following example.

Declare
  z number;
Begin
  z := ‘&x’ + ‘&y’;
  dbms_output.put_line (‘Result: ‘ || z);
Exception
  When VALUE_ERROR then
    dbms_output.put_line (‘Invalid values’);
End ;

The above program accepts the value of ‘x’ and ‘y’ in the form of character strings and implicitly converts them to numeric. If user provides a non-numeric value, the VALUE_ERROR gets raised. In this case, INVALID_NUMBER will not be raised as nothing is related with SQL statement.

{mospagebreak title=A Word About OTHERS Exception}

This is a great exception which can handle almost any type of exception within PL/SQL. In general this exception is used when you are not sure about any specific error which may raise within PL/SQL block. But you are suggested to use this exception caution. This exception is very frequently used for log eventing or auditing. The following example illustrates usage of this exception.

Declare
  v_empno emp.empno%type:=&empno;
  v_ename emp.ename%type:=’&ename’;
  v_deptno emp.deptno%type:=&deptno;
Begin
  Insert into emp(empno,ename,deptno)
  Values (v_empno,v_ename,v_deptno);
  dbms_output.put_line(‘Employee Successfully Inserted’);
Exception
  When OTHERS then
    dbms_output.put_line(‘An error occurred. Please check your values’);
End ;

You should also observe from the above example that the error message is also bit generic rather than confirming to only one scenario (like previous example). It is suggested to provide a generic error message within OTHERS exception, as you may not know any specific error which may occur.

[gp-comments width="770" linklove="off" ]

chat sex hikayeleri Ensest hikaye