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