Manejo de errores en PL/SQL

PL/SQL posee un extraordinario manejo de errores, facilitando la detección y el procesamiento de los mismos, a esto se le llaman EXCEPTIONS. Al producirse un error PL/SQL lanza una excepción y el procesamiento normal se interrumpe. A partir de ese punto el control se transfiere a la sección del código llamada EXCEPTIONS que se encuentra al final de cualquier bloque PL/SQL.

Cada tipo de error que pueda ocurrir en tiempo de ejecución podrá ser manejado por una excepción particular. Existen EXCEPTIONS para cualquier tipo de error que se pueda presentar y también un tipo de excepción que puede atajar cualquier error que ocurra e impedir que la aplicación reviente de forma no controlada.

PL/SQL maneja excepciones predefinidas que se lanzan automáticamente para ciertas condiciones de error comunes. Por ejemplo, si se intenta realizar una consulta en una tabla y no trae ningún resultado PL/SQL lanza automáticamente la excepción predefinida llamada NO_DATA_FOUND.

PL/SQL también nos permite declarar nuestras propias excepciones, cualquier condición que consideremos es un error podrá ser declarada de esa forma y estaremos en capacidad de mostrar mensajes de error al usuario, son sumamente convenientes.

Excepciones predefinidas en PL/SQL

ACCESS_INTO_NULL: se intenta asignar valores a los atributos de un objeto no inicializado.

CASE_NOT_FOUND: no se selecciona ninguna de las opciones de las cláusulas WHEN de una sentencia CASE, y no hay cláusula ELSE.

COLLECTION_IS_NULL: la aplicación intenta asignar valores a los elementos de una tabla anidada o varray no inicializado.

CURSOR_ALREADY_OPEN: una aplicación intenta abrir un cursor que ya está abierto. Tendría que cerrarse el cursor antes de poder reabrirse.

DUP_VAL_ON_INDEX: una aplicación intenta almacenar valores duplicados en una columna que posee un constrain de unique index.

INVALID_CURSOR: la aplicación intenta realizar una operación sobre un cursor que no es válida como cerrar un cursor que no se ha abierto previamente.

INVALID_NUMBER: ocurre cuando una conversión de una cadena de caracteres en un número falla porque la cadena no representa un número válido.

LOGIN_DENIED: error que ocurre cuando se intenta iniciar sesión con una contraseña o usuario inválido.

NO_DATA_FOUND: este error se produce cuando al tratar de hacer una consulta el resultado no regresa ninguna fila.

NOT_LOGGED_ON: se intenta hacer una operación en la base de datos sin estar conectado.

ROWTYPE_MISMATCH: error que se presenta cuando el valor a asignar y la variable que lo contendrá son de tipos incompatibles. Esto también lo veremos con parámetros que pasemos a subprogramas de tipo inesperado.

SUBSCRIPT_BEYOND_COUNT: Una aplicación referencia a una tabla anidada o a un elemento de varray utilizando un número de índice mayor que el número de elementos de la colección.

SUBSCRIPT_OUTSIDE_LIMIT: Una aplicación referencia a una tabla anidada o un elemento de varray utilizando un número de índice ilegal.

TOO_MANY_ROWS: este error aparece cuando un SELECT INTO regresa más de una fila.

VALUE_ERROR: se produce un error aritmético, de conversión, de truncamiento o de restricción de tamaño.

ZERO_DIVIDE: se intenta dividir un número entre cero.

Vamos a crear un programa en el que buscaremos a un empleado el cual filtraremos por el campo EMPNO, sin el manejo de excepciones pertinentes la aplicación se reventaría. Trabajaremos el NO_DATA_FOUND:

DECLARE 
            lV_EMPNO VARCHAR2(30);
            LV_ENAME VARCHAR(30);
        BEGIN
            SELECT EMPNO, ENAME INTO LV_EMPNO, LV_ENAME FROM EMP
            WHERE EMPNO = 1;
        EXCEPTION
            WHEN NO_DATA_FOUND THEN -- manejamos el error 
                DBMS_OUTPUT.PUT_LINE ('no existe el usuario');
        END;

Resultado:

No existe el usuario

Statement processed.

0.01 seconds

Vamos a crear un programa muy parecido al anterior, pero la diferencia está en que nos va a regresar más de una fila y los cursores implícitos solo pueden devolver una fila. Vamos a atajar el error con la exception TOO_MANY_ROWS y vamos a impedir que reviente la aplicación.

DECLARE
        lV_EMPNO VARCHAR2(30);
        LV_ENAME VARCHAR(30);
    BEGIN
        SELECT EMPNO, ENAME INTO LV_EMPNO, LV_ENAME FROM EMP;    
    EXCEPTION
        WHEN TOO_MANY_ROWS THEN -- menejo de errores
            DBMS_OUTPUT.PUT_LINE ('La consulta devuelve más de una fila');
    END;

Resultado:

La consulta devuelve más de una fila

Statement processed.

0.01 seconds

En esta oportunidad para intencionalmente a producir un error tratando de dividir el número 100 entre 0, pero vamos a manejar correctamente la excepción ZERO_DIVIDE:

BEGIN    
        DBMS_OUTPUT.PUT_LINE (100 / 0);
    EXCEPTION
        WHEN ZERO_DIVIDE THEN
            DBMS_OUTPUT.PUT_LINE ('no se puede dividir entre cero');
    END;
     

Resultado:

No se puede dividir entre cero

Statement processed.

0.01 seconds

Declaración de Excepciones

Nuestras propias excepciones tendrán que ser declaradas en la parte declarativa del bloque PL/SQL, subprograma o paquete. Le daremos un nombre, seguido por la palabra clave EXCEPTION. En el siguiente ejemplo vamos a hacer una consulta en una tabla y vamos a filtrar el resultado por el id, si el número es nulo o menos que tres queremos levantar un error. En cambio, al colocar el número 101 que sí existe en la tabla imprimimos en pantalla el resultado.

DECLARE 
        LV_CUSTOMER_ID OEHR_CUSTOMERS.CUSTOMER_ID%TYPE:=2; 
        LV_CUST_FIRST_NAME OEHR_CUSTOMERS.CUST_FIRST_NAME%TYPE; 
        LV_CUST_LAST_NAME OEHR_CUSTOMERS.CUST_LAST_NAME%TYPE;  
        -- Nuestra propia excepción
        mi_excepcion  EXCEPTION; 
     BEGIN 
        IF LV_CUSTOMER_ID IS NULL OR LV_CUSTOMER_ID < 3 THEN 
           RAISE mi_excepcion; 
        ELSE 
           SELECT  CUSTOMER_ID, CUST_FIRST_NAME, CUST_LAST_NAME INTO  LV_CUSTOMER_ID, LV_CUST_FIRST_NAME, LV_CUST_LAST_NAME 
           FROM OEHR_CUSTOMERS 
           WHERE CUSTOMER_ID = LV_CUSTOMER_ID;
           DBMS_OUTPUT.PUT_LINE ('ID: '||  LV_CUSTOMER_ID);
           DBMS_OUTPUT.PUT_LINE ('NOMBRE: '||  LV_CUST_FIRST_NAME);  
           DBMS_OUTPUT.PUT_LINE ('APELLIDO: ' || LV_CUST_LAST_NAME); 
        END IF; 
      
     EXCEPTION 
        WHEN mi_excepcion THEN 
           DBMS_OUTPUT.put_line('No puede ser nulo, ni menor que tres (3)'); 
        WHEN NO_DATA_FOUND THEN 
           DBMS_OUTPUT.put_line('No existe en la base de datos');
     END;
      

Resultado:

No puede ser nulo, ni menor que tres (3)

Statement processed.

0.01 seconds