Sei sulla pagina 1di 11

Ejemplos de Trigger

Tenemos una tabla de archivos (llamada archivos), en la que uno de los campos es un
MD5 sobre el contenido del archivo (campo llamado dmd5). En una tabla anexa (llamada
aux), necesitamos llevar la cuenta de cuntos registros tenemos con el mismo MD5, para
esto tenemos dos campos: dmd5 y cont. As, cada vez que insertamos un registro en
archivos debemos de actualizar el contador correspondiente en aux. De igual manera,
cuando borramos un registro de archivos, deberemos de decrementar el contador
correspondiente en aux y en caso de que sea el nico, eliminar el registro.
Obviamente esto se puede hacer desde el mismo programa de actualizacin, pero resulta, y
es este caso en particular lo que motiv el empleo de triggers, que son varios programas los
que actualizan esta tabla, as que mantener cada uno de ellos se vuelve un tanto cuanto
engorroso. La mejor solucin es emplear triggers. Cabe sealar que el ejemplo que se
muestra a continuacin funciona slo de la versin de PostgreSQL 6.5.3 en adelante, dado
que emplea el Procedure Language PL/pgSQL y algunas particularidades integradas a
partir de esa versin.
Bueno, el cdigo para actualizar los valores es el siguiente:
DROP FUNCTION inc_aux ();
CREATE FUNCTION inc_aux () RETURNS OPAQUE AS '
DECLARE
myrec record;
BEGIN
SELECT * INTO myrec FROM aux WHERE aux.dmd5 = NEW.dmd5;
IF NOT FOUND THEN
INSERT INTO aux VALUES (NEW.dmd5, 1);
ELSE
UPDATE aux SET cont=cont+1 WHERE dmd5 = NEW.dmd5;
END IF;
RETURN NEW;
END;
' LANGUAGE 'plpgsql';
DROP TRIGGER ins_arc ON archivos;
CREATE TRIGGER ins_arc BEFORE INSERT ON archivos FOR EACH ROW
EXECUTE PROCEDURE inc_aux();

Los DROPs antes de crear la funcin y el trigger, son para garantizar que las funciones no
existen previamente y sobre todo, porque tuve que hacer un demonial de pruebas antes de
que la cosa jalara.
Recordemos que los triggers no pueden recibir argumentos y siempre tienen que regresar
un valor opaco.
El select se hace para averiguar si existe un registro con el MD5 y en caso de no existir lo
insertamos con el contador en uno y en caso de que ya exista, incrementamos el contador

en uno. Muy simple. Slo es de notar que tuvimos que declarar un registro para hacer la
consulta y saber si el registro existe en la tabla anexa para actualizarlo (update) o si ser
necesario crear uno nuevo (insert). El registro NEW se refiere al registro con que el que fu
disparado el trigger.
Eliminamos el trigger para, en caso de que ya exista uno con el mismo nombre, garantizar
que se ejecutar el que vamos a declarar. Como podemos ver, los triggers estn asociados a
tablas y por eso se debe de indicar de que tabla lo eliminamos al momento de efectuar el
drop.
Ahora veamos el caso en que se eliminan registros. Por supuesto, en este caso estamos
eliminando un registro que est en la base, as que no necesitamos ver primero si existe. El
nico considerando a tomar en cuenta es el caso en que el MD5 es nico, en cuyo caso
habremos de eliminar de la tabla dmd5 el registro.
DROP FUNCTION dec_aux ();
CREATE FUNCTION dec_aux () RETURNS OPAQUE AS '
BEGIN
UPDATE aux SET cont=cont-1 WHERE dmd5 = OLD.dmd5;
DELETE FROM aux where cont < 1;
RETURN NULL;
END;
' LANGUAGE 'plpgsql';
DROP TRIGGER del_arc ON archivos;
CREATE TRIGGER del_arc AFTER DELETE ON archivos
FOR EACH ROW EXECUTE PROCEDURE dec_aux();

Una manera de probarlo, es la siguiente:


mancha=> SELECT * FROM aux WHERE dmd5='12345678901234567890123456789012';
dmd5|cont
----+---(0 rows)
mancha=> INSERT INTO archivos VALUES ('BORRAME','/',12345,'31-121999','23:59:07',
'12345678901234567890123456789012','12345678901234567890123456789012','Ca
serola');
INSERT 10981731 1
mancha=> SELECT * FROM aux WHERE dmd5='12345678901234567890123456789012';
dmd5|cont
--------------------------------+---12345678901234567890123456789012|
1
(1 row)
mancha=> delete from archivos where arc='BORRAME';
DELETE 1
mancha=> SELECT * FROM aux WHERE dmd5='12345678901234567890123456789012';
dmd5|cont
----+---(0 rows)

Ahora supongamos que tenemos un pequeo sistema de nmina y en el mdulo de ABC11.1


queremos tener la garanta mnima de que no se insertar un registro sin nombre o con

salario negativo. Adems, queremos llevar nota de quin y cuando modific los registros.
Sea la tabla:
CREATE TABLE emp (
nombre text,
salario int4,
last_date datetime,
last_user name);

Definimos la funcin que ``estampe'' los cambios:


CREATE FUNCTION emp_stamp () RETURNS OPAQUE AS '
BEGIN
-- Verifica que el nombre y el salario se hallan dado
IF NEW.nombre ISNULL THEN
RAISE EXCEPTION ''nombre no puede ser NULL'';
END IF;
IF NEW.salario ISNULL THEN
RAISE EXCEPTION ''% no puede tener un salario NULL'',
NEW.nombre;
END IF;
-- Que no tenga salario negativo (puede ser cero)
IF NEW.salario < 0 THEN
RAISE EXCEPTION ''% no puede tener un salario negativo'',
NEW.nombre;
END IF;
-- Ahora estampamos quin y cundo hizo los cambios
NEW.last_date := ''now'';
NEW.last_user := getpgusername();
RETURN NEW;
END;
' LANGUAGE 'plpgsql';

Creamos el trigger:
CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
FOR EACH ROW EXECUTE PROCEDURE emp_stamp();

Y vemos un ejemplo:
mancha=> insert into emp values ('Meiga Pintita', 2500);
INSERT 11635171 1
mancha=> insert into emp values ('Misha Negrita', 2500);
INSERT 11635172 1
mancha=> select * from emp;
nombre
|salario|last_date
|last_user
-------------+-------+----------------------------+--------Bill Clinton |
1000|Mon 03 Jan 12:28:12 2000 CST|mancha
Boris Yeltsin|
2000|Mon 03 Jan 12:28:36 2000 CST|mancha
Meiga Pintita|
2500|Wed 05 Jan 13:07:12 2000 CST|mancha
Misha Negrita|
2500|Wed 05 Jan 13:08:37 2000 CST|mancha
(4 rows)
mancha=> insert into emp values ('Perico Cerillo', -1000000);
ERROR: Perico Cerillo no puede tener un salario negativo

mancha=> insert into emp values (NULL , 1000000);


ERROR: nombre no puede ser NULL
mancha=> insert into emp values ('Perico Cerillo', NULL);
ERROR: Perico Cerillo no puede tener un salario NULL

Sin embargo, se nos olvid hacer una verificacin bsica:


mancha=> insert into emp values ('', 1000000);
INSERT 11635173 1
mancha=> select * from emp;
nombre
|salario|last_date
|last_user
-------------+-------+----------------------------+--------Bill Clinton |
1000|Mon 03 Jan 12:28:12 2000 CST|mancha
Boris Yeltsin|
2000|Mon 03 Jan 12:28:36 2000 CST|mancha
Meiga Pintita|
2500|Wed 05 Jan 13:07:12 2000 CST|mancha
Misha Negrita|
2500|Wed 05 Jan 13:08:37 2000 CST|mancha
|1000000|Wed 05 Jan 13:09:50 2000 CST|mancha
(5 rows)

Queda como ejercicio verificar que no se inserten nombres en blanco.

CONCEPTO Y EJEMPLO DE USO Y CREACIN DE DISPARADORES


(TRIGGERS) EN ORACLE
CONCEPTO
Un disparador define una accin que la base de datos debe llevar a cabo cuando se produce
algn suceso relacionado con la misma. Los disparadores (triggers) pueden utilizarse para
completar la integridad referencial, tambin para imponer reglas de negocio complejas o
para auditar cambios en los datos. El cdigo contenido en un disparador, denominado
cuerpo del disparador, est formado por bloques PL/SQL. La ejecucin de disparadores es
transparente al usuario.
Para crear un disparador (trigger) en una tabla, el usuario con el que accedamos a Oracle
deber ser propietario de la misma, teniendo as el privilegio ALTER para la tabla ALTER
ANY TABLE. Adems, dicho usuario, debe disponer del privilegio CREATE TRIGGER.
Existen varios tipos de disparadores, dependiendo del tipo de transaccin de disparo y el
nivel en el que se ejecuta el disparador (trigger):
1.- Disparadores de nivel de fila: se ejecutan una vez para cada fila afectada por una
instruccin DML. Los disparadores de nivel de fila se crean utilizando la clusula for each
row en el comando create trigger.

2.- Disparadores de nivel de instruccin: se ejecutan una vez para cada intruccin DML.
Por ejemplo, si una nica intruccin INSERT inserta 500 filas en una tabla un disparador de
nivel de instruccin para dicha tabla slo se ejecutar una vez. Los disparadores de nivel de
instruccin son el tipo predeterminado que se crea con el comando create trigger.
3.- Disparadores Before y After: puesto que los disparadores son ejecutados por sucesos,
puede establecerse que se produzcan inmediatamente antes (before) o despus (after) de
dichos sucesos.
4.- Disparadores Instead Of: puede utilizar INSTEAD OF para indicar a Oracle lo que
tiene que hacer en lugar de realizar las acciones que invoca el disparador. Por ejemplo,
podra usar un disparador INSTEAD OF en una vista para gestionar las inserciones en una
tabla o para actualizar mltiples tablas que son parte de una vista.
5.- Disparadores de esquema: puede crear disparadores sobre operaciones en el nivel de
esquema tales como create table, alter table, drop table, audit, rename, truncate y revoke.
Puede incluso crear disparadores para impedir que los usuarios eliminen sus propias tablas.
En su mayor parte, los disparadores de nivel de esquema proporcionan dos capacidades:
impedir operaciones DDL y proporcionar una seguridad adicional que controle las
operaciones DDL cuando star se producen.
6.- Disparadores en nivel de base de datos: puede crear disparadores que se activen al
producirse sucesos de la base de datos, incluyendo errores, inicios de sesin, conexiones y
desconexiones. Puede utilizar este tipo de disparador para automatizar el mantenimiento de
la base de datos o las acciones de auditora.
EJEMPLO DE CREACIN DE TRIGGER (DISPARADOR) DE NIVEL DE FILA
1.- En primer lugar abriremos la Consola de Oracle Enterprise Manager:

seleccionaremos "Iniciar en Modo Autnomo" y pulsaremos en "Aceptar":

Selecionaremos la Base de Datos del rbol e introduciremos un usuario y contrasea con


permisos suficientes para crear trigger en la tabla de ejemplo (en nuestro caso accederemos
como "system"):

2.- En el rbol accederemos a "Esquema" y a continuacin seleccionaremos el usuario


propietario de la tabla en la que crearemos el trigger, en nuestro caso "ALONSO". Despus
pulsaremos en "Tablas", seleccioremos la tabla a la que queramos aplicarle el disparador y,
sobre la carpeta "Disparadores" pulsaremos con el botn derecho del ratn, nos aparecer
un men emergente, pulsaremos "Crear...":

3.- En "Nombre" introduciremos el nombre del disparador y en "Cuerpo del Disparador"


introduciremos el cdigo PL/SQL que queramos que se ejecute, en nuestro caso queremos
que el disparador aada un registro otra tabla llamada "resultadodisparador" cuando el valor
que introduzca el usuario en el campo CAMPO2 de la tabla PRUEBA (despus de insertar
una nueva fila) sea superior a 1000:

En la pestaa "Evento" de la ventana de "Crear Disparador" marcaremos "Disparador en


Tabla", marcaremos "Despus" y marcaremos tambin en "Arrancar Disparador:"
"Insertar":

Pulsaremos en "Crear" y, si no ha habido errores el el cdigo PL/SQL nos aparecer un


mensaje como este "Disparador se ha creado correctamente":

Nota: tambin se pueden crear disparadores sin necesidad de utilizar la interfaz grfica
(Consola de Oracle Enterprise Manager). Se pueden crear mediante SQL. La consulta SQL
que crea el disparador anterior es la siguiente:
CREATE OR REPLACE TRIGGER "ALONSO"."INSERCIONNUMEROMAYOR1000"
AFTER
INSERT ON "ALONSO"."PRUEBA" FOR EACH ROW WHEN (new.CAMPO2 > 1000)
BEGIN
insert into resultadodisparador (fecha, aviso, tabla)
values

(Sysdate, 'Registro con CAMPO2 superior a 1000',


'PRUEBA');
END;
Con este disparador, cuando un usuario inserte un registro en la tabla PRUEBA cuyo
CAMPO2 tenga un valor superior a 1000 se insertar automticamente (transparente para el
usuario) otro registro en la tabla "resultadodisparador" con la fecha de la insercin
(sysdate), el aviso "Registro con CAMPO2 superior a 1000" y el nombre de la tabla origen
del disparador "PRUEBA".
Obviamente, para que este disparador funcione correctamente debern existir las tablas
PRUEBA (origen del disparador) y "resultadodisparador" (donde se insertar el registro si
se cumple la condicin CAMPO2 > 1000.
La consulta SQL necesaria para crear la tabla "PRUEBA":
CREATE TABLE "ALONSO"."PRUEBA" (
"CAMPO1" VARCHAR2(10) NOT NULL,
"CAMPO2" NUMBER)
La consulta SQL necesaria para crear la tabla "resultadodisparador":
CREATE TABLE "ALONSO"."RESULTADODISPARADOR" ("FECHA" DATE NOT
NULL, "AVISO" VARCHAR2(100) NOT NULL, "TABLA" VARCHAR2(50)
NOT NULL)
En nuestro ejemplo estamos trabajando en todo momento utilizando el usuario ALONSO
para la creacin del disparador y de las tablas, puesto que es el propietario de dichos
objetos.
Posibles errores:
Si tras crear el disparador aparece con un icono rojo con una X en medio es porque el
cdigo PL/SQL no es correcto:

En ocasiones Oracle introduce algn "END" y ";" de ms al final. Para solucionarlo es


suficiente con quitar los "END" y ";" sobrantes y volver a compilar y guardar el disparador
hasta que quede con el siguiente icono:

Para comprobar que el disparador funciona adecuadamente, realizamos una insercin en la


tabla PRUEBA:
insert into alonso.prueba values ('PRUEBA 1', 10)
con esta insercin el disparador no se ejecutar pues CAMPO2 es inferior a 1000, para
comprobarlo hacemos un SELECT a la tabla RESULTADODISPARADOR (no nos debera
devolver ningn registro):
select * from alonso.resultadodisparador
Ahora realizamos una insercin en la tabla PRUEBA con el valor del CAMPO2 superior a
1000:
insert into alonso.prueba values ('PRUEBA 2', 1500)
Ahora comprobamos con un SELECT que la tabla "resultadodisparador" tiene una nueva
fila:
select * from alonso.resultadodisparador

ALGUNAS CONSULTAS SQL PARA MODIFICAR EL ESTADO DE UN TRIGGER


Para eliminar un trigger mediante SQL:
drop trigger nombretrigger
Para deshabilitar temporalmente un trigger (dejar de realizar su funcin):
alter trigger nombretrigger disable
Para habilitar un trigger deshabilitado:
alter trigger nombretrigger enable
Para deshabilitar todos los triggers asociados a una tabla:
alter table nombretabla disable all triggers
Para habilitar todos los triggers asociados a una tabla:
alter table nombretabla enable all triggers