Excepciones, Triggers
Excepciones, Triggers
Excepciones, Triggers
Gestión de excepciones
Se llama excepción a todo hecho que le sucede a un programa que causa que la
ejecución del mismo finalice. Lógicamente eso causa que el programa termine de forma
anormal. Las excepciones se deben a:
Que ocurra un error detectado por Oracle (por ejemplo si un SELECT no devuelve datos
ocurre el error ORA-01403 llamado NO_DATA_FOUND). Que el propio programador las
lance (comando RAISE). Las excepciones se pueden capturar a fin de que el programa
controle mejor la existencia de las mismas.
La captura se realiza utilizando el bloque EXCEPTION que es el bloque que está justo
antes del END del bloque. Cuando una excepción ocurre, se comprueba el bloque
EXCEPTION para ver si ha sido capturada, si no se captura, el error se propaga a Oracle
que se encargará de indicar el error existente.
DECLARE
sección de declaraciones
BEGIN
instrucciones
EXCEPTION
END;
Cuando ocurre una determinada excepción, se comprueba el primer WHEN para
comprobar si el nombre de la excepción ocurrida coincide con el que dicho WHEN
captura; si es así se ejecutan las instrucciones, si no es así se comprueba el siguiente
WHEN y así sucesivamente.
Oracle tiene muchas excepciones predefinidas. Son errores a los que Oracle asigna un
nombre de excepción. Algunas de las que aparecen con mayor frecuencia son:
DECLARE
x NUMBER := 0;
y NUMBER := 3;
res NUMBER;
BEGIN
res:=y/x;
DBMS_OUTPUT.PUT_LINE(res);
EXCEPTION
DBMS_OUTPUT.PUT_LINE('Error inesperado') ;
END;
En el siguiente ejemplo el cursor implícito Hotel99 sólo puede recibir una única fila o
registro como resultado de una consulta. En este caso podrían producirse 2
excepciones: NO_DATA_FOUND (la consulta select no devuelve ningún registro) o
TO_MANY_ROWS (la consulta select devuelve más de un registro). En el primer caso
insertamos un nuevo registro. En el segundo caso borramos el registro duplicado.
DECLARE
Hotel99 Hotel%ROWTYPE;
BEGIN
EXCEPTION
END;
Si una instrucción SELECT INTO no devuelve una fila, PL/SQL lanza la excepción
predefinida NO_DATA_FOUND tanto si se comprueba SQL%NOTFOUND en la línea
siguiente como si no. Si una instrucción SELECT INTO devuelve más de una fila,
PL/SQL lanza la excepción predefinida TOO_MANY_ROWS tanto si se comprueba
SQL%ROWCOUNT en la línea siguiente como si no.
5.4.8.3. Funciones de uso con excepciones
Ejemplo:
EXCEPTION
...
DBMS_OUTPUT.PUT_LINE
END;
El programador puede lanzar sus propias excepciones simulando errores del programa.
Para ello hay que:
miExcepcion EXCEPTION;
RAISE miExcepcion;
EXCEPTION
...
...
Ejemplo:
DECLARE
error_al_eliminar EXCEPTION;
BEGIN
IF SQL%NOTFOUND THEN
RAISE error_al_eliminar;
END IF;
EXCEPTION
END;
DECLARE
BEGIN
IF SQL%NOTFOUND THEN
END IF;
END;
PROCEDURE nombre IS
PROCEDURE nombre
{IS|AS}
Los procedimientos permiten utilizar parámetros para realizar su tarea. El modo, que es
opcional, puede ser de 3 tipos: IN, OUT o IN OUT. Si no se indica nada, por defecto es
IN.
Parámetros IN. Son los parámetros que en otros lenguajes se denominan como
parámetros por valor. El procedimiento recibe una copia del valor o variable
que se utiliza como parámetro al llamar al procedimiento. Estos parámetros
pueden ser: valores literales (18 por ejemplo), variables (v_num por ejemplo) o
expresiones (como v_num+18). A estos parámetros se les puede asignar un
valor por defecto.
Parámetros OUT. Relacionados con el paso por variable de otros lenguajes.
Sólo pueden ser variables y no pueden tener un valor por defecto. Se utilizan
para que el procedimiento almacene en ellas algún valor. Es decir, los
parámetros OUT son variables sin declarar que se envían al procedimiento de
modo que si en el procedimiento cambian su valor, ese valor permanece en
ellas cuando el procedimiento termina.
Parámetros IN OUT. Son una mezcla de los dos anteriores. Se trata de
variables declaradas anteriormente cuyo valor puede ser utilizado por el
procedimiento que, además, puede almacenar un valor en ellas. No se las
puede asignar un valor por defecto.
CREATE [ OR REPLACE ]
BEGIN
procedimiento1;
procedimiento2();
procedimiento3(parametro1, parametro2);
...
END;
o también en SQL*Plus:
EXEC procedimiento1;
EXEC procedimiento2();
Ejemplo:
CREATE OR REPLACE
PROCEDURE muestra_fecha IS
fecha DATE;
BEGIN
END muestra_fecha;
BEGIN
muestra_fecha;
END;
o también en SQL*Plus:
EXEC muestra_fecha;
CREATE OR REPLACE
IS
BEGIN
DBMS_OUTPUT.PUT_LINE(texto);
END;
BEGIN
ESCRIBE('HOLA');
END;
o también en SQL*Plus:
EXEC ESCRIBE('HOLA');
Al declarar cada parámetro se indica el tipo de los mismos, pero no su tamaño; es decir
sería VARCHAR2 y no VARCHAR2(50).
5.4.10. Funciones
FUNCTION nombre
RETURN tipoDeDatos
{IS|AS}
CREATE [ OR REPLACE ]
La opción REPLACE hace que si ya existe una función con ese nombre, se reemplaza
con la que se crea ahora. Los parámetros son la lista de variables que necesita la función
para realizar su tarea. Para invocar la función debemos hacerlo dentro de una expresión.
Ejemplo:
Ejemplo:
CREATE OR REPLACE
RETURN NUMBER
IS
BEGIN
RETURN NUM1+NUM2;
END SUMA;
Para invocar la función definida debemos hacerlo dentro de una expresión. Ejemplos:
Los paquetes sirven para agrupar bajo un mismo nombre funciones y procedimientos.
Facilitan la modularización de programas y su mantenimiento. Los paquetes constan de
dos partes:
Especificación. Que sirve para declarar los elementos de los que consta el
paquete. En esta especificación se indican los procedimientos, funciones y
variables públicos del paquete (los que se podrán invocar desde fuera del
paquete). De los procedimientos sólo se indica su nombre y parámetros (sin el
cuerpo).
Cuerpo. En la que se especifica el funcionamiento del paquete. Consta de la
definición de los procedimientos indicados en la especificación. Además se
pueden declarar y definir variables y procedimientos privados (sólo visibles
para el cuerpo del paquete, no se pueden invocar desde fuera del mismo).
-- PACKAGE_ARITMETICA.SQL
CREATE OR REPLACE
PACKAGE aritmetica IS
PROCEDURE mostrar_info;
END aritmetica;
-- PACKAGE_BODY_ARITMETICA.SQL
CREATE OR REPLACE
PROCEDURE mostrar_info IS
BEGIN
DBMS_OUTPUT.PUT_LINE
END mostrar_info;
FUNCTION suma (a NUMBER, b NUMBER) RETURN NUMBER IS
BEGIN
RETURN (a+b);
END suma;
BEGIN
RETURN (a-b);
END resta;
BEGIN
RETURN (a*b);
END multiplica;
BEGIN
RETURN (a/b);
END divide;
END aritmetica;
BEGIN
ARITMETICA.MOSTRAR_INFO;
END;
DECLARE
num1 NUMBER:= 2;
num2 NUMBER:= 5;
resultado NUMBER;
BEGIN
ARITMETICA.MOSTRAR_INFO;
DBMS_OUTPUT.PUT_LINE
('La suma de ' || num1 ||' y '|| num2 ||' es '|| resultado);
DBMS_OUTPUT.PUT_LINE
('La resta de ' || num1 ||' y '|| num2 ||' es '|| resultado);
END;
Oracle incorpora una serie de paquetes para ser utilizados dentro del código PL/SQL.
Es el caso del paquete DBMS_OUTPUT que sirve para utilizar funciones y
procedimientos de escritura como PUT_LINE. Otro ejemplo es el paquete
DBMS_RANDOM, que contiene diversas funciones para utilizar número aleatorios.
Quizá la más útil es la función DBMS_RANDOM.RANDOM que devuelve un número
entero (positivo o negativo) aleatorio (y muy grande). Ejemplos:
MOD(ABS(DBMS_RANDOM.RANDOM),10)+1
-- Entre 20 y 50
MOD(ABS(DBMS_RANDOM.RANDOM),31)+20
5.4.12. Disparadores (Triggers)
TRIGGER Nombre
Bloque_del_TRIGGER;
Eso permite en una sola instrucción operar con todos los triggers relacionados con una
determinada tabla (es decir actúa sobre los triggers que tienen dicha tabla en el apartado
ON del trigger).
Aquí sólo veremos los del primer y segundo tipo. Por lo que se dará por hecho en todo
momento que nos referiremos siempre a ese tipo de triggers.
Puesto que un trigger es un código que se dispara, al crearle se deben indicar las
siguientes cosas:
INSERT
UPDATE
DELETE
BEFORE
AFTER
INSTEAD OF
Ejemplo:
CREATE OR REPLACE
TRIGGER Control_Empleados
BEGIN
END Control_Empleados;
TABLA VARCHAR2(50),
USUARIO VARCHAR2(50),
FECHA DATE
);
CREATE [ OR REPLACE ]
TRIGGER Nombre
[ DECLARE
declaraciones ]
BEGIN
cuerpo
[ EXCEPTION
captura de excepciones ]
END;
Los eventos asocian el trigger al uso de una instrucción DML. En el caso de la instrucción
UPDATE, el apartado OF hace que el trigger se ejecute sólo cuando se modifique la
columna indicada (o columnas si se utiliza una lista de columnas separada por comas).
En la sintaxis del trigger, el apartado OR permite asociar más de un evento al trigger (se
puede indicar INSERT OR UPDATE por ejemplo).
Ejemplo:
CREATE OR REPLACE
TRIGGER ins_personal
BEGIN
RAISE_APPLICATION_ERROR
END IF;
END;
Este trigger impide que se puedan añadir registros a la tabla de personal si no estamos
entre las 10 y las 13 horas.
Sintaxis básica:
CREATE [ OR REPLACE ]
TRIGGER Nombre
declaraciones ]
BEGIN
cuerpo
[ EXCEPTION
captura de excepciones ]
END;
La cláusula FOR EACH ROW hace que el trigger sea de fila, es decir que se repita su
ejecución por cada fila afectada en la tabla por la instrucción DML. El apartado WHEN
permite colocar una condición que deben de cumplir los registros para que el trigger se
ejecute. Sólo se ejecuta el trigger para las filas que cumplan dicha condición.
Cuando se ejecutan instrucciones UPDATE, hay que tener en cuenta que se modifican
valores antiguos (OLD) para cambiarles por valores nuevos (NEW). Las palabras NEW
y OLD permiten acceder a los valores nuevos y antiguos respectivamente. En el
apartado de instrucciones del trigger (BEGIN …END) serían :NEW.nombre y
:OLD.nombre. Imaginemos que deseamos hacer una auditoría sobre una tabla en la que
tenemos un listado de las piezas mecánicas que fabrica una determinada empresa. Esa
tabla es PIEZAS y contiene el tipo y el modelo de la pieza (los dos campos forman la
clave de la tabla) y el precio de venta de la misma. Deseamos almacenar en otra tabla
diferente los cambios de precio que realizamos a las piezas, para lo cual creamos la
siguiente tabla:
precio_viejo NUMBER(11,4),
precio_nuevo NUMBER(11,4),
tipo VARCHAR2(2),
modelo NUMBER(2),
fecha DATE
);
CREATE OR REPLACE
TRIGGER hacer_auditoria_piezas
BEGIN
VALUES (
:OLD.precio_venta,
:NEW.precio_venta,
:OLD.tipo,
:OLD.modelo,
SYSDATE );
END hacer_auditoria_piezas;
Con este trigger cada vez que se modifiquen un registros de la tabla de piezas, siempre
y cuando se esté incrementado el precio, se añade una nueva fila por registro modificado
en la tabla de auditorías, observar el uso de NEW y de OLD y el uso de los dos puntos
(:NEW y :OLD) en la sección ejecutable. Cuando se añaden registros, los valores de
OLD son todos nulos. Cuando se borran registros, son los valores de NEW los que se
borran.
Son palabras que se utilizan para determinar la instrucción DML que se estaba
realizando cuando se lanzó el trigger. Esto se utiliza en triggers que se lanza para varias
operaciones (utilizando INSERT OR UPDATE por ejemplo). En ese caso se pueden
utilizar sentencias IF seguidas de INSERTING, UPDATING o DELETING; éstas
palabras devolverán TRUE si se estaba realizando dicha operación.
CREATE OR REPLACE
TRIGGER nombre
BEGIN
IF DELETING THEN
ELSE
END IF;
END;
Hay un tipo de trigger especial que se llama INSTEAD OF y que sólo se utiliza con las
vistas. Una vista es una consulta SELECT almacenada. En general sólo sirven para
mostrar datos, pero podrían ser interesantes para actualizar, por ejemplo en esta
declaración de vista:
AS
ORDER BY p.tipo,p.modelo,e.n_almacen;
Indicando que esa operación no es válida en esa vista (al utilizar dos tablas). Esta
situación la puede arreglar un trigger que inserte primero en la tabla de piezas (sólo si
no se encuentra ya insertada esa pieza) y luego inserte en existencias.
Eso lo realiza el trigger de tipo INSTEAD OF, que sustituirá el INSERT original por el
código indicado por el trigger:
CREATE OR REPLACE
TRIGGER ins_piezas_exis
BEGIN
VALUES(:NEW.tipo,:NEW.modelo,:NEW.precio);
INSERT INTO existencias(tipo,modelo,n_almacen,cantidad)
VALUES(:NEW.tipo,:NEW.modelo, :NEW.almacen,:NEW.cantidad);
END;
Este trigger permite añadir a esa vista añadiendo los campos necesarios en las tablas
relacionadas en la vista. Se podría modificar el trigger para permitir actualizar, eliminar
o borrar datos directamente desde la vista y así cualquiera desde cualquier acceso a la
base de datos utilizaría esa vista como si fuera una tabla más. Orden de ejecución de
los triggers
Puesto que sobre una misma tabla puede haber varios triggers, es necesario conocer
en qué orden se ejecutan los mismos. El orden es:
Errores de compilación
Es frecuente que cometamos algún tipo de error cuando definimos distintos tipos de
bloques. Podemos consultar los errores de compilación mediante la vista
USER_ERRORS;
PROCEDURE
FUNCTION
PACKAGE
PACKAGE BODY
TRIGGER
Por ejemplo, para ver los errores producidos en el siguiente trigger:
CREATE OR REPLACE
TRIGGER Control_Empleados
BEGIN
END Control_Empleados;
SHOW ERRORS;