Curso de PL/SQL 9.2.2 CREACION DE TRIGGERS ROW Como ya hemos dicho, los triggers 'row' se ejecutan una vez para cada registro afectado por la sentencia DML que lo disparó. Es decir: si realizamos un UPDATE de varios registros, el código del trigger se ejecuta una vez para cada registro modificado. Este tipo de triggers se usa mucho para realizar acciones automatizadas como pueden ser, por ejemplo, cálculos de importes acumulados, etc... o acciones automatizadas derivadas de la actualización y/o borrado de registros, como por ejemplo al modificar un campo de domiciliación bancaria de un cliente, cambiar los recibos pendientes de remesar de ese cliente para que se giren por el nuevo banco. Disponemos, además, de los valores “viejo” y “nuevo” de cada campo de cada registro que se ve afectado para poder interactuar y/o decidir qué acciones realizamos dentro del cuerpo del disparador. La sintaxis de un trigger a nivel de registro es: CREATE [OR REPLACE] TRIGGER nombre_trigger BEFORE|AFTER evento ON nombre_tabla [REFERENCING OLD AS nombre_viejo | NEW AS nombre_nuevo] FOR EACH ROW [WHEN condicion] bloque PL/SQL ; La clausula REFERENCING especifica (si es incluye) los “prefijos” para utilizar los valores viejos y nuevos de los campos del registro actual que queramos usar en el cuerpo del trigger. Si no la escribimos, estos prefijos son por defecto :OLD y :NEW Evidentemente la clausula FOR EACH ROW especifica que éste es un trigger a nivel de registro (“para cada registro”). La clausula WHEN especifica una restricción del trigger. La condición es evaluada por cada registro para determinar si se ejecuta el cuerpo del trigger para ese registro o no. Vamos a ver un ejemplo de este tipo de triggers: Crearemos un trigger que nos auditará las inserciones, borrados y modificaciones en la tabla empleados. Ya vimos el mismo ejemplo pero sin importar cuantas inserciones/borrados/actualizaciones se habían realizado (trigger statement). Simplemente grabábamos un registro de auditoría de que se había realizado una acción. Esta vez, grabaremos un registro de auditoría para cada registro afectado y además guardaremos los valores “viejo” y “nuevo” de los campos. Algo muy útil para seguir el rastro de cuando, quién y qué se ha hecho en nuestra tabla ¿a que si?. Pág 1 de 5 Curso de PL/SQL CREATE OR REPLACE TRIGGER audita_emp_valores AFTER INSERT OR UPDATE OR DELETE ON empleados FOR EACH ROW DECLARE v_usuario VARCHAR2(50); v_accion VARCHAR2(15); v_codigo empleados.codigo%type; BEGIN --- Recuperamos el usuario de sistema operativo --- Ojo!! hay que tener permisos para hacer SELECT sobre la --- tabla v$session SELECT osuser INTO v_usuario FROM v$session WHERE audsid = USERENV('SESSIONID'); --- Determinamos que acción es y qué codigo --- de empleado debemos grabar IF INSERTING THEN v_accion := 'INSERCION'; v_codigo := :new.codigo ; ELSIF DELETING THEN v_accion := 'BORRADO'; v_codigo := :old.codigo ; ELSIF UPDATING THEN v_accion := 'MODIFICACION'; v_codigo := :old.codigo ; END IF; --- Finalmente insertamos el registro de auditoría --INSERT INTO AUDIT_EMP_DETALLE(usuario, fecha, accion, codigo, nombre_viejo, nombre_nuevo, dni_viejo, dni_nuevo, depart_viejo, depart_nuevo, salario_viejo, salario_nuevo, fecha_alta_vieja, fecha_alta_nueva, jefe_viejo, jefe_nuevo) VALUES (v_usuario, sysdate, v_accion, v_codigo, :old.nombre, :new.nombre, :old.dni, :new.dni, :old.departamento, :new.departamento, :old.salario, :new.salario, :old.fecha_alta, :new.fecha_alta, :old.jefe_viejo, :new.jefe.nuevo); EXCEPTION WHEN OTHERS THEN RAISE_APPLICATION_ERROR(-20400, 'Error al insertar auditoría. Empleado: '||to_char(v_codigo)); END; / Pág 2 de 5 Curso de PL/SQL Unas puntualizaciones a tener en cuenta en este código: – Determinar el código de empleado a grabar (v_codigo) es debido a que si estamos insertando, el valor :old.codigo es NULL ya que no existía antes. Por el contrario si estamos borrando, el valor :new.codigo es NULL. En el caso de modificación grabamos el viejo porque al ser clave primaria NO se puede modificar, así que nos da igual cual grabar. En caso de haber desactivado las constraints para modificarlo (hay usuarios muy avanzados que te montan la de Dios en un momento), tendremos auditado el codigo viejo y en el registro de la base de datos el valor del nuevo. – Al no poner la clausula REFERENCING OLD AS ni el NEW AS, se toman por defecto :old y :new. Podríamos haber especificado otros nombres. – La clausula FOR EACH ROW es la que determina que el trigger es “para cada registro”. – Hay quien dirá que crear tablas de auditoría duplicando los campos de cada tabla es tarea demasiado costosa. Cierto. Pero hay otras opciones. Podemos crear las tablas de auditoría iguales a las originales, añadiéndoles sólo los campos “usuario, fecha y accion” y grabar dos registros de auditoría para cada acción en lugar de uno. El primero con los valores “viejos” y el segundo con los valores “nuevos”. Unicamente tendremos que modificar el INSERT del trigger del ejemplo y añadir otro INSERT más. Es más, sólo en el caso de una actualización haría falta grabar dos registros. En una inserción y en un borrado nos basta con un registro de auditoría ya que en el primer caso no tenemos valores “viejos” y en el segundo no tenemos valores “nuevos”. Dejo como ejercicio el modificar el trigger anterior para que haga lo que acabamos de comentar. Por último, podemos (como hemos dicho antes) condicionar un trigger row mediante la clausula WHEN. ¿Para qué es esto?. Muy sencillo. Hemos comentado que una sentencia DML que afecta a un grupo de registros, provocará la ejecución del código del trigger para cada uno de esos registros. Pues con la clausula WHEN podemos excluir algunos de ellos mediante una (o varias) condiciones. En el ejemplo anterior, podríamos sólo auditar las acciones sobre un empleado (pongamos por caso: 'Pepito Pérez' cuyo codigo es el 45) simplemente poniendo: CREATE OR REPLACE TRIGGER audita_emp_valores AFTER INSERT OR UPDATE OR DELETE ON empleados FOR EACH ROW WHEN (new.codigo=45 OR old.codigo=45) IMPORTANTE: Daros cuenta que los calificadores prefijados con dos puntos en la clausula WHEN. “new” y “old” no van Pág 3 de 5 Curso de PL/SQL 9.3 CONCEPTOS FINALES SOBRE TRIGGERS Vamos a ver algunas cosas para terminar con triggers, como puede ser las diferencias entre trigger y procedimiento almacenado, triggers de BD y triggers de aplicación, cómo activar o desactivar un trigger y, por supuesto, cómo borrarlos. Diferencias entre trigger y procedimientos almacenados – – La sintaxis, por supuesto. CREATE TRIGGER y CREATE PROCEDURE. Los triggers se llaman de forma ímplicita (el usuario provoca su disparo de forma transparente) y los procedimientos almacenados se llaman de forma explícita (el usuario los invoca directamente) – Las sentencias COMMIT, ROLLBACK y SAVEPOINT no están permitidas dentro del cuerpo de un trigger. Dentro de un procedimiento almacenado sí. – Aunque haya algún error de compilación al crear el trigger, éste se crea (en estado “no válido”, mejor dicho: “desactivado”). Diferencias entre trigger de BD y trigger de aplicación – – – – – Un trigger de BD es ejecutado por acciones desde cualquier herramienta de la BD o de aplicación. Un trigger de aplicación sólo se ejecuta dentro de dicha aplicación. El trigger de BD es ejecutado por una sentencia SQL de manipulación de datos. Un trigger de aplicación puede ser ejecutado mediante la navegación entre campos, presionando una tecla, o mediante otras acciones (eventos). Dos clases de triggers de BD: Statement y row. Los trigger de aplicación no distinguen. Durante la ejecución de un trigger de BD, ante un fallo, se realiza un ROLLBACK sobre la sentencia que disparó el trigger. Si el trigger es de aplicación debemos controlar nosotros dónde y hasta dónde hacer un ROLLBACK mediante código. Un trigger de BD se ejecuta bajo el dominio de seguridad del autor del trigger. Un trigger de aplicación se ejecuta bajo el dominio de seguridad del usuario conectado a la BD mediante la aplicación. (Detalle sutil pero de mucha importancia). Activar y desactivar triggers de base de datos Un trigger de base de datos puede estar activado ó desactivado. Cuando se crea por primera vez, y si no hay ningún fallo de compilación, se activa automáticamente. Con ALTER TRIGGER nombre_trigger DISABLE desactivamos el trigger “nombre_trigger”. | ENABLE activamos ó Con ALTER TABLE nombre_tabla DISABLE | ENABLE ALL TRIGGERS activamos ó desactivamos TODOS los trigger de la tabla “nombre_tabla”. Con ALTER TRIGGER nombre_trigger COMPILE recompilamos un trigger y lo activamos si la compilación es correcta. Borrar triggers de la base de datos Como era de esperar: DROP TRIGGER nombre_trigger Pág 4 de 5 Curso de PL/SQL Resumiendo: La implementación añaden muchas características: – – – – – – de triggers de base de datos nos Seguridad: Los triggers permiten el acceso a tablas según el valor de los datos Auditorías: Los triggers “siguen” los valores de las operaciones de datos sobre las tablas. Los motores de BD que tiene características de auditoría, normalmente “siguen” las operaciones de datos sobre las tablas. Integridad de datos: Con los trigger podemos implementar reglas complejas de integridad adicionalmente a las que posea la BD. Replicación de tablas: Mediante trigger podemos replicar de forma síncrona las tablas deseadas. Datos derivados: Mediante triggers de BD podemos implementar el cálculo de datos derivados en tablas, permitiendo así una actualización automática de dischos datos. Control de eventos: Los triggers controlan eventos de forma transparente. 10. AGRADECIMIENTOS Como siempre a mi mujer por su paciencia, a los usuarios del foro de hackxcrack por su interés y sus opiniones y, finalmente, a mi sobrina Alba que, con tres meses y medio, ya apunta un “interés” por todo lo tecnológico (todo lo quiere coger, jejeje) 11. BIBLIOGRAFIA Introducción al PL/SQL – Oracle Corp. Desarrollo de aplicaciones con PL/SQL, Volúmenes I y II – Oracle Corp. Apuntes de los cursos de Certificación Oracle – Jorge Navarrete. PL/SQL User's Guide and Reference - Oracle Corp. Using Oracle 8 – Tiger. Este manual puede ser distribuido y/o reproducido total o parcialmente en cualquier medio siempre y cuando se cite al autor y la fecha abajo citadas: Jorge Navarrete Olmos jorge@navarreteolmos.com Abril de 2004 Valencia Pág 5 de 5