powered by Jorge Muñoz 1 Triggers en Oracle Nuestro objetivo

Anuncio
Triggers en Oracle
Nuestro objetivo para este artículo es intentar mostrar un breve
panorama de cómo crear TRIGGERS (disparadores o gatilladores [1])
para bases de datos bajo Oracle 8, asi como describir la funcionalidad
tan versatil que proporcionan al programador. Por lo tanto es de
esperar que el lector tenga un poco de experiencia con entornos Oracle
y conozca la notación básica de bases de datos, de igual manera serán necesarios unos
mínimos conocimientos del lenguaje PL/SQL.
1. Definición de trigger
Los disparadores son procedimientos que se ejecutan cuando se produce un suceso de
base de datos (una operación DML: INSERT, UPDATE o DELETE) en una tabla
específica. El acto de ejecutar un disparador se conoce como disparo.
2. Uso de los triggers
Los disparadores pueden emplearse para muchas cosas diferentes, incluyendo:
• El mantenimiento de restricciones de integridad complejas, que no sean posibles con
las restricciones declarativas definidas en el momento de crear la tabla.
• La auditoría de la información contenida en una tabla, registrando los cambios
realizados y la identidad del que los llevó a cabo.
• El aviso automático a otros programas de que hay que llevar a cabo una determinada
acción, cuando se realiza un cambio en una tabla.
3. Sintaxis general de trigger
Lo presentado en este apartado se ha extraído desde [2].
La sintaxis general para crear un disparador es:
CREATE [OR REPLACE] TRIGGER nombre_disparador
{ BEFORE | AFTER ? suceso_disparo ON referencia_tabla
[FOR EACH ROW [ WHEN condición_disparo ]]
cuerpo_disparador,
donde nombre_disparador es el nombre del disparador, suceso_disparo especifica
cuándo se activa el disparador, referencia_tabla es la tabla para la cual se define el
disparador y cuerpo_disparador es el código principal del disparador. Antes se evalúa la
condición_disparo incluida en la cláusula WHEN, si es que está presente. El cuerpo del
disparador se ejecuta sólo cuando dicha condición se evalúa como verdadera.
4. Componentes de un disparador
Los componentes de un disparador son el nombre, el suceso de disparo y el cuerpo. La
cláusula WHEN es opcional.
powered by Jorge Muñoz
1
5.1 Nombres de disparadores
El espacio de nombres para los disparadores es diferente del de otros subprogramas. El
espacio de nombres es el conjunto de identificadores válidos que pueden emplearse
como nombres de un objeto.
Los disparadores existen en un espacio de nombres separado del de los procedimientos,
paquetes y tablas, por lo que un disparador puede tener el mismo nombre que una tabla
o procedimiento. Sin embargo dentro de un mismo esquema deben tener nombres
diferentes entre sí.
5.2 Tipos de disparadores
El suceso de disparo determina el tipo de disparador. Los disparadores pueden definirse
para las operaciones INSERT, UPDATE o DELETE, y pueden dispararse antes o
después de la operación. Finalmente, el nivel de los disparadores puede ser la fila o la
orden.
La Figura 1 muestra los tipos de disparadores:
Categoría
Valores
Comentarios
INSERT,
Define qué tipo de orden DML provoca la
Orden
DELETE,
activación del disparador.
UPDATE
Define si el disparador se activa antes o
BEFORE
Temporización
después de que se ejecute la orden
o AFTER
(disparador previo o posterior).
Los disparadores con nivel de fila se
activan una vez por cada fila afectada por
la orden que provocó el disparo. Los
Fila u
disparadores con nivel de orden se activan
Nivel
orden
sólo una vez, antes o después de la orden.
Los disparadores con nivel de fila se
identifican por la cláusula FOR EACH
ROW en la definición del disparador.
Figura 1. Tipos de disparadores [2]
Disparadores de sustitución
Oracle 8 proporciona un tipo adicional de disparador. Los disparadores de sustitución (
INSTEAD OF ) pero tienen una serie de restricciones [3]:
• INSTEAD OF es una cláusula válida solo para vistas; no se puede especificar un
disparador INSTEAD OF en una tabla.
• Si una vista tiene un disparador INSTEAD OF, cualquier vista creada sobre ésta debe
tener a su vez un disparador INSTEAD OF.
• Cuando definimos disparadores INSTEAD OF para columnas LOB, podemos leer
powered by Jorge Muñoz
2
tanto el seudo-registro :OLD como el seudo-registro :NEW, pero no se puede modificar
sus valores.
5.3 Restricciones de los disparadores
El cuerpo de un disparador es un bloque PL/SQL. Cualquier orden que sea legal en un
bloque PL/SQL, es legal en el cuerpo de un disparador, con las siguientes restricciones:
1. Un disparador no puede emitir ninguna orden de control de transacciones: COMMIT,
ROLLBACK o SAVEPOINT. El disparador se activa como parte de la ejecución de la
orden que provocó el disparo, y forma parte de la misma transacción que dicha orden.
Cuando la orden que provoca el disparo es confirmada o cancelada, se confirma o
cancela también el trabajo realizado por el disparador.
2. Por razones idénticas, ningún procedimiento o función llamado por el disparador
puede emitir órdenes de control de transacciones.
3. El cuerpo del disparador no puede contener ninguna declaración de variables LONG
o LONG RAW.
4. Existen restricciones acerca de a qué tablas puede acceder el cuerpo de un disparador.
Dependiendo del tipo de disparador y de las restricciones que afecten a las tablas.
5. Eliminación y deshabilitación de los disparadores
La sintaxis de la orden que elimina un disparador es [4]:
DROP TRIGGER nombre_disparador;
donde nombre_disparador es el nombre del disparador que se desea eliminar. Esta orden
elimina el disparador de forma permanente del diccionario de datos.
La sintaxis de la orden que deshabilita un disparador es [4]:
ALTER TRIGGER nombre_disparador [DISABLE | ENABLE]
donde nombre_disparador es el nombre del disparador. Todos los disparadores están
habilitados en el momento de su creación. Cuando se deshabilita un disparador continua
existiendo en el diccionario de datos.
También se puede habilitar o deshabilitar todos los disparadores de una tabla
determinada, utilizando la orden ALTER TABLE con la cláusula ENABLE ALL
TRIGGERS o DISABLE ALL TRIGGERS.
6. Orden de activación de los disparadores
Lo presentado en este apartado se ha extraído desde [2].
Los disparadores se activan al ejecutarse la orden DML. El algoritmo de ejecución de
una orden DML es el siguiente:
1. Ejecutar, si existe, el disparador de tipo BEFORE (disparador previo) con nivel de
powered by Jorge Muñoz
3
orden.
2. Para cada fila a la que afecte la orden:
a. Ejecutar, si existe, el disparador de tipo BEFORE con nivel de fila.
b. Ejecutar la propia orden.
c. Ejecutar, si existe, el disparador de tipo AFTER (disparador posterior) con nivel de
fila.
3. Ejecutar, si existe, el disparador de tipo AFTER con nivel de orden.
7. Utilización de :old y :new en los disparadores con nivel de fila
Un disparador con nivel de fila se ejecuta una vez por cada fila procesada por la orden
que provoca el disparo. Dentro del disparador puede accederse a la fila que está siendo
actualmente procesada utilizando, para ello, dos seudo-registros, :old y :new.
La Figura 2 muestra este concepto:
Orden de
disparo
INSERT
UPDATE
DELETE
:old
:new
No definido; todos los
campos toman valor
NULL.
Valores originales de la
fila, antes de la
actualización.
Valores que serán
insertados cuando se
complete la orden.
Nuevos valores que serán
escritos cuando se complete
la orden.
No definidos; todos los
Valores, antes del borrado
campos toman el valor
de la fila.
NULL.
Figura 2. :old y :new [2]
El tipo de estos seudo-registros es :
tabla_disparo%ROWTYPE;
donde tabla_disparo es la tabla sobre la que se ha definido el disparador.
Los registros :new y :old son sólo válidos dentro de los disparadores con nivel de fila. Si
se intenta Hacer referencia a cualquiera de los dos dentro de un disparador con nivel de
orden, se obtendrá un error de compilación. Puesto que un disparador con nivel de
orden se ejecuta una sola vez, incluso si la orden procesa varias filas, entonces :old y
:new no tienen ningún sentido ya que no se sabe a que fila se están refiriendo.
8. La cláusula WHEN
Lo presentado en este apartado se ha extraído desde [2].
La cláusula WHEN sólo es válida para los disparadores con nivel de fila. Si está
powered by Jorge Muñoz
4
presente, el cuerpo del disparador sólo se ejecutará para las filas que cumplan la
condición especificada en la cláusula.
La cláusula WHEN tiene la forma:
WHEN condición
donde condición es una expresión booleana que será evaluada para cada fila. Se puede
hacer también referencia a los registros :new y :old dentro de la condición, pero en ese
caso no se utilizan los dos puntos.
9. Utilización de predicados de los disparadores: INSERTING, UPDATING y
DELETING
Dentro de un disparador en el que se disparan distintos tipos de órdenes DML
(INSERT, UPDATE y DELETE), hay tres funciones booleanas que pueden emplearse
para determinar de qué operación se trata. Estos predicados son INSERTING,
UPDATING y DELETING. Su comportamiento es el siguiente:
La Figura 3 muestra el comportamiento de INSERTING, UPDATING y DELETING
Predicado
Comportamiento
TRUE si la orden de disparo es INSERT; FALSE en
INSERTING
otro caso.
TRUE si la orden de disparo es UPDATE; FALSE en
UPDATING
otro caso.
TRUE si la orden de disparo es DELETE; FALSE en
DELETING
otro caso.
Figura 3. INSERTING, UPDATING y DELETING [2]
10. Ejemplo
Un ejemplo de su uso es proporcionar una facilidad de auditoría donde se realiza
automáticamente un registro de actividades siempre que se cambia una fila de una tabla.
Sin los disparadores de bases de datos esta funcion sería implementada en los
programas de seccion de entrada (front-end) que realizan el cambio en la base de datos;
sin embargo alguien que se pase por alto el código de los programas de sección de
entrada (utilizando SQL*Plus, por ejemplo) no pasaría por las comprobaciones y el
procesamiento definidos.
Supóngase que tenemos una tabla llamada SAL (en la que almacenamos los salarios de
los empleados de la empresa) y resulta necesario conocer cuándo esta siendo accedida la
tabla y el tipo de operación que se realiza. El ejemplo que presentamos a continuación
contiene un paquete de muestra que rastrea esta información registrando la hora y la
accion ejecutada (UPDATE, DELETE, o INSERT) en la tabla SAL. Mediante una
variable global, STAT.ROWCNT, inicializada a cero por el trigger BEFORE e
incrementada cada vez que un disparador con nivel de fila es ejecutado, tenemos la
powered by Jorge Muñoz
5
informacion estadística que necesitamos salvar en el disparador AFTER.
DROP TABLE stat_tab;
CREATE TABLE stat_tab(utype CHAR(8),
rowcnt INTEGER, uhour INTEGER);
CREATE OR REPLACE PACKAGE stat IS
rowcnt INTEGER;
END;
CREATE TRIGGER bt BEFORE UPDATE OR DELETE OR INSERT ON sal
BEGIN
stat.rowcnt := 0;
END;
CREATE TRIGGER rt BEFORE UPDATE OR DELETE OR INSERT ON sal
FOR EACH ROW BEGIN
stat.rowcnt := stat.rowcnt + 1;
END;
CREATE TRIGGER at AFTER UPDATE OR DELETE OR INSERT ON sal
DECLARE
typ CHAR(8);
hour NUMBER;
BEGIN
IF updating
THEN typ := 'update'; END IF;
IF deleting THEN typ := 'delete'; END IF;
IF inserting THEN typ := 'insert'; END IF;
hour := TRUNC((SYSDATE - TRUNC(SYSDATE)) * 24);
UPDATE stat_tab
SET rowcnt = rowcnt + stat.rowcnt
WHERE utype = typ
AND uhour = hour;
IF SQL%ROWCOUNT = 0 THEN
INSERT INTO stat_tab VALUES (typ, stat.rowcnt, hour);
END IF;
EXCEPTION
WHEN dup_val_on_index THEN
UPDATE stat_tab
SET rowcnt = rowcnt + stat.rowcnt
WHERE utype = typ
AND uhour = hour;
powered by Jorge Muñoz
6
END;
11. Conclusiones
Las restriciones de integridad complejas que refieren a varias tablas y atributos no
pueden ser especificadas en las definiciones de tablas. En cambio, los triggers proveen
una técnica procedural para especificar y mantener restricciones de integridad. Permiten
especificar restricciones más complejas ya que un trigger es esencialmente un
procedimiento PL/SQL. Ahora bien, si es posible, deben usarse restricciones para la
comprobacion; son más rápidas que usar disparadores de base de datos.
Gracias a la posibilidad de tener multiples disparadores del mismo tipo para el mismo
comando sobre cualquier tabla se permite la instalacion modular de aplicaciones que
tienen triggers sobre las mismas tablas.
En un trigger no se debe incluir mucho codigo sino que resulta mas eficiente poner los
segmentos de codigo mas largos dentro de un procedimiento y, después, llamar al
procedimiento desde el disparador de la base de datos (los procedimientos son
almacenados en una forma compilada).
Los triggers son administrados de manera centralizada. Se codifican una sola vez, se
prueban y se fuerza a que todas las aplicaciones pasen por su control.
Los triggers deben ser controlados, o al menos auditados, por un administrador de BDs
powered by Jorge Muñoz
7
Descargar