disparadores en oracle en oracle bibliografía oracle® database concepts - 10g release 2 (10.2)...
Post on 15-Jul-2018
235 Views
Preview:
TRANSCRIPT
Disparadores en ORACLE
BibliografíaOracle® Database Concepts - 10g Release 2 (10.2) (Octubre 2005)
Oracle® Database Application Developer's Guide – Fundamentals 10g Release 2 (10.2) (Noviembre 2005)
Óscar Díaz – Universidad del País Vasco (UPV)Bases de Datos Activas
Introducción
PL/SQL: lenguaje de programación estructurado en bloques
Bloques: unidad mínima en PL/SQL
Soportan DML y DDL
Anónimos / Con nombre → TRIGGERS
Disparadores (triggers) en Oracle: bloques de código que son implícitamente invocados cuando algo sucede
Triggers VS Procedimientos
Ejecución Implícita: Disparar
No admiten argumentos
Aplicaciones:
Restricciones (Constraints)
Auditorías
Informar de Eventos
DECLARE ▬ optional
BEGIN ▬ required
EXCEPTION ▬ optional
END; required
/
CREATE PROCEDURE Get_emp_rec
(Emp_number IN
Emp_tab.Empno%TYPE) AS
BEGIN
- - - - -
END;
/
CREATE TRIGGER
NombreTrigger
BEFORE INSERT ON StarsIn
DECLARE
…..
END;
/ TRIGGER
PROCEDIMIENTO
Introducción
3 Tipos
DML/DDL (Fila/Sentencia, BEFORE/AFTER)
INSTEAD OF
SYSTEM
Estructura General de un Disparador
CREATE [OR REPLACE] TRIGGER nombre
{BEFORE | AFTER | INSTEAD OF} Temporalidad del Evento
{INSERT | DELETE | UPDATE [OF <atributo>]} ON <tabla>
[WHEN condición] Granularidad[FOR EACH ROW | STATEMENT]
BEGIN
cuerpo del trigger
END;
Evento
Condición
Acción
Temporalidad del Evento: AFTER / BEFORE
BEFORE
Ejecutan la acción asociada antes de que la
sentencia sea ejecutada
Decidir si la acción debe realizarse
Utilizar valores alternativos para la sentenciaCREATE TRIGGER NombreTrigger
BEFORE Insert ON NombreTabla ….
AFTER
Ejecutan la acción asociada después de que
se haya ejecutado la sentenciaCREATE TRIGGER NombreTrigger
AFTER Insert ON NombreTabla ….
Los de tipo BEFORE son
mejores en términos de
rendimiento
Granularidad del Evento:
FOR EACH ROW / STATEMENT
A NIVEL DE FILA:
ROW TRIGGERS
Ejecutan la acción asociada tantas veces como
filas se vean afectadas por la sentencia que lo
dispara
Si ninguna fila se ve afectada, no se disparaCREATE TRIGGER NombreTrigger
BEFORE Insert ON NombreTabla
FOR EACH ROW ….
Utilizar cuando la acción depende de la sentencia
que produjo el evento o de las filas afectadas
Granularidad del Evento:
FOR EACH ROW / STATEMENT
A NIVEL DE SENTENCIA:
STATEMENT TRIGGERS
Ejecutan una única vez la acción asociada,
independientemente del número de filas que se
vean afectadas por la sentenciaCREATE TRIGGER NombreTrigger
BEFORE Insert ON NombreTabla
[STATEMENT]
Utilizar cuando la acción NO depende de la sentencia
que produjo el evento o de las filas afectadas
Comprobaciones de seguridad a cerca del usuario o el
momento concreto
Generar registros de auditoría
Condición
Expresa una condición que debe cumplirse en el
momento de producirse el evento, para que la acción sea
ejecutada.WHEN persona.nombre = 'pepe' OR persona.edad > 35
Se puede utilizar cualquier combinación de operadores
lógicos (AND, OR, NOT) y relacionales (< <= > >= = <>).
No se puede especificar una condición para los
disparadores a nivel de sentencia (STATEMENT) ni los
disparadores INSTEAD OF
Debe ser una consulta SQL y no puede contener
subconsultas SELECT * FROM Productos
WHERE PrecioUnidad IN (SELECT PrecioUnidad
FROM DetallePedido
WHERE Descuento = 0 .25)
Orden de Ejecución
Una sentencia SQL puede disparar varios TRIGGERS.
La activación de un trigger puede disparar la activación de otros triggers.
1. Triggers Before (nivel de sentencia)
2. Para cada fila:
1. Trigger Before (a nivel de fila)
2. Ejecuta la Sentencia
3. Triggers After (a nivel de fila)
3. Triggers After (a nivel de Sentencia)
Se compromete o se deshace toda la transacción
El orden de ejecución de disparadores del mismo tipo es indeterminado
Estructura General de un Disparador
CREATE OR REPLACE TRIGGER Control_DocenciaAFTER DELETE ON Profesores
WHEN old.nombre LIKE ‘%Juan%’ FOR EACH ROW
DECLAREvar VARCHAR2(50);
BEGINSELECT a.nombre INTO var FROM Asignaturas a WHERE REF(a) = :NEW.asignatura;
DELETE FROM Asignaturas WHERE nombre = varEND;/
Evento
Condición
Acción
Correlation Identifiers: Valores OLD y NEW
Tipo especial de variable PL/SQL tratada como un registro de tipo
tabla_modificada%ROWTYPE
Con OLD.nombre_columna referenciamos:
Al valor que tenía la columna antes del cambio debido a una modificación
(UPDATE)
Al valor de una columna antes de una operación de borrado sobre la
misma (DELETE)
Al valor NULL para operaciones de inserción (INSERT)
Con NEW.nombre_columna referenciamos:
Al valor de una nueva columna después de una operación de inserción
(INSERT)
Al valor de una columna después de modificarla mediante una sentencia de
modificación (UPDATE)
Al valor NULL para una operación de borrado (DELETE)
Condición (WHEN ….) OLD, NEW
En el cuerpo del disparador :OLD, :NEWSINTAXIS
ROW TRIGGER: ejemplo
Persona
Cod Nombre Edad
C1
C2
C3
C4
C5
María
Pepe
Pepe
Luisa
Pepe
25
40
45
48
22
Persona2
Cod Nombre Edad
C1
C2
C3
C4
C5
María
Pepe
Pepe
Luisa
Pepe
25
40
45
48
22
Cuando se borre en la tabla persona alguna persona que
se llame “pepe” o cuya edad sea mayor de 35 años,
eliminar también dicha persona de la tabla persona2
DELETE FROM persona
WHERE cod in (‘C1’,‘C3’,‘C4’) Borra C3 y C4de persona2
STATEMENT TRIGGER: ejemplo
Borra 3 tuplas y se emiteun único mensaje
Socio
Cod Nombre Fecha_ant
S1
S2
S3
S4
S5
María
Pepe
Pepe
Luisa
Pepe
......
......
......
......
......
Cuando se borre en la tabla socio emitir un mensaje
indicando que no se pueden borrar socios
DELETE FROM socio
WHERE nombre = ‘Pepe’
Triggers DML
Disparados por sentencias DML:
INSERT, UPDATE o DELETE
Todas las filas o sólo algunas (WHEN)
ISBN GENERO TÍTULO
100-09-89 Novela El Quijote
----- ---- ----
GENERO TOTAL_LIBROS
Novela 50
Infantil 15
CREATE OR REPLACE TRIGGER UpdateEstadisticasGenero
AFTER INSERT OR DELETE OR UPDATE ON Libros
DECLARE
UDDATE Estadisticas SET ….
BEGIN
----------------------
END UpdateEstadisticasGenero;
/
LIBROS ESTADÍSTICAS
INSERT INTO EmpleadoDpatoVALUES (‘Carlos Gómez', ‘Contabilidad-1’);
ERROR en línea 1:ORA-01779: no se puede modificar una columna que se corresponde con una tabla no reservada por clave
Triggers INSTEAD OF
Sólo sobre VISTAS
DNI NOMBRE DEPARTAMENT
O
11111111 José García CT-1
----- ---- ----
NOMBRE CÓDIGO
Contabilidad - 1 CT-1
Recursos
Humanos
RRHH
EMPLEADO DEPARTAMENTO
CREATE VIEW EmpleadoDpto as
SELECT e.nombre, d.nombre FROM Empleado E, Departamento D
WHERE E.Departamento = D.Codigo;
CREATE OR REPLACE TRIGGER InsertEmepleadoDpto
INSTEAD OF INSERT ON EmpleadoDpto
DECLARE- - --BEGIN
INSERT INTO Empleado VALUES …INSERT INTO Departamento VALUES …
END;
Triggers de Sistema
Disparados por eventos del Sistema o eventos
relacionados con las acciones de los Usuarios
Sistema
Arranque y parada: STARTUP, SHUTDOWN
Transacciones: COMMIT, ROLLBACK
Errores: SERVERERROR
Usuarios
Login / Logoff
Sentencias DDL: CREATE, ALTER,
DROP
CREATE OR REPLACE TRIGGER LogCreations
AFTER CREATE ON SCHEMA
BEGIN
INSERT INTO LogCreates (user_id, object_type,
object_name, object_owner, creation_date)
VALUES (USER, ORA_DCIT_OBJ_TYPE,
ORA_DICT_OBJ_NAME,ORA_DICT_OBJ_OWNER, SYSDATE)
END LogCreations;
/
BEFORE/AFTER Triggers: ejemplo
CREATE OR REPLACE TRIGGER GenerarAutorID
BEFORE INSERT OR UPDATE ON Autores
FOR EACH ROW
BEGIN
SELECT id_autores INTO :new.ID FROM Tabla_IDs;
UPDATE Tabla_IDs SET id_autores = id_autores + 1;
END GenerarAutorID;
/
INSERT INTO autores (nombre, apellidos)
VALUES ('Lolita', 'Lazarus');
INSERT INTO autores (ID, nombre, apellidos)
VALUES (-7, 'Zelda', 'Zoom');
Funciones del Cuerpo del Disparador
CREATE OR REPLACE TRIGGER ejemplo
BEFORE INSERT OR UPDATE OR DELETE ON tabla
BEGIN
IF DELETING THEN
Acciones asociadas al borrado
ELSIF INSERTING THEN
Acciones asociadas a la inserción
ELSIF UPDATING[(‘COL1’)]
Acciones asociadas a la modificación
ELSIF UPDATING[(‘COL2’)]
Acciones asociadas a la modificación
END IF;
END ejemplo;
/
Inserting, Deleting, Updating
Elevar excepciones en el cuerpo del Disparador
RAISE_APPLICATION_ ERROR
(nro_error, mensaje); [-20000 y -20999]
CREATE OR REPLACE TRIGGER ejemplo
BEFORE DELETE ON tabla
FOR EACH ROW
BEGIN
IF tabla.columna= valor_no_borrable THEN
RAISE_APPLICATION_ERROR(-20000,‘La fila no se puede borrar’);
END IF;
...
END ejemplo;
Declaración de Variables
nombre CONSTANT NUMBER:=valor;
nombre TIPO;
nombre nombretabla.nombrecolumna%TYPE;
nombre nombretabla%ROWTYPE
CREATE...
BEFORE...
[FOR EACH ROW ...]
DECLARE
Declaración de
variables
BEGIN
Activar / Desactivar disparadores
Todos los disparadores asociados a una tabla:ALTER TABLE nombre_tabla ENABLE ALL TRIGGERS
ALTER TABLE nombre_tabla DISABLE ALL TRIGGERS
(Por defecto, todos están activados al crearse)
Un disparador específico:
ALTER TRIGGER nombre_disparador ENABLE
ALTER TRIGGER nombre_disparador DISABLE
Consultar información sobre los disparadores
Eliminar un disparador
DROP TRIGGER nombre_disparador;
Ver todos los disparadores y su estado SELECT TRIGGER_NAME, STATUS FROM USER_TRIGGERS;
Ver el cuerpo de un disparadorSELECT TRIGGER_BODY
FROM USER_TRIGGERS
WHERE TRIGGER_NAME='nombre_disparador';
Ver la descripción de un disparadorSELECT DESCRIPTION
FROM USER_TRIGGERS
WHERE TRIGGER_NAME= 'nombre_disparador';
Ejemplo
CREATE TRIGGER Ejemplo
AFTER DELETE ON tabla1
FOR EACH ROW
WHEN ((OLD.nombre=’pepe’) OR (OLD.edad > 35))
BEGIN
DELETE FROM tabla2WHERE tabla2.cod=:OLD.cod;
END Ejemplo;
/
SELECT Trigger_type, Triggering_event, Table_name
FROM USER_TRIGGERS
WHERE Trigger_name = 'Ejemplo';
TYPE TRIGGERING_STATEMENT TABLE_NAME
---------------- ------------------------ ----------
AFTER EACH ROW DELETE tabla1
SELECT Trigger_body FROM USER_TRIGGERS
WHERE Trigger_name = 'Ejemplo';
TRIGGER_BODY
----------------------------------
BEGIN
DELETE FROM tabla2 WHEREtabla2.cod=:OLD.cod;
END Ejemplo;
/
Restricciones: tablas mutantes
Tabla mutante (mutating)
tabla que está siendo modificada por una operación
DML
tabla que se verá afectada por los efectos de un
DELETE CASCADE debido a la integridad referencial
(hasta Oracle8i).
Las órdenes del cuerpo de un disparador (de tipo
FOR EACH ROW) no pueden …
Leer o actualizar una tabla mutante que esté en la
propia declaración del disparador
MUTATING TABLE ERROR RUNTIME ERROR
Tablas Mutantes: ejemplo
CREATE OR REPLACE TRIGGER trigger_asignaturas
BEFORE INSERT OR UPDATE ON asignaturas
FOR EACH ROW
DECLARE
v_total NUMBER;
v_nombre VARCHAR2(30);
BEGIN
SELECT COUNT(*)INTO v_total FROM asignaturas -- ASIGNATURAS está MUTANDO
WHERE DNI = :NEW.DNI;
-- comprueba si el profesor está sobrecargado
IF v_total >= 10 THEN
SELECT nombre||' '||apellidos INTO v_nombre FROM profesores
WHERE DNI = :NEW.DNI;
RAISE_APPLICATION_ERROR (-20000, ‘El profesor '||
v_nombre||', está sobrecargado');
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR (-20001,
‘Datos de profesor incorrectos');
END;
Tablas Mutantes: ejemplo
UPDATE asignaturas
SET DNI = ‘000000000’
WHERE asignaturas_id = ‘BD’;
UPDATE section
*
ERROR at line 1:
ORA-04091: table BD_XX.ASIGNATURAS is mutating,
trigger/function may not see it
ORA-06512: at "BD_XX.TRIGGER_ASIGNATURAS", line 5
ORA-04088: error during execution of trigger
'BD_XX.TRIGGER_ASIGNATURAS'
SELECT COUNT(*)
INTO v_total
FROM asignaturas
WHERE DNI = :NEW.DNI;
Tablas Mutantes: solución
Crear 2 disparadores
En el disparador a nivel de fila (for each row)
almacenamos los datos que queremos consultar
(los que provocan el error de tabla mutante)
En el disparador a nivel de orden (statement)
realizamos la consulta (usando los datos almacenados)
La mejor forma de almacenar los valores es utilizar un
paquete (opcionalmente, podríamos utilizar una tabla)
Tablas Mutantes: solución
En el trigger a nivel de fila guardaremos el DNI y el
nombre del profesor
Necesitamos 2 variables globales para que esos datos
estén disponibles más adelante, al ejecutar el trigger a
nivel de fila
Por lo tanto, creamos un paquete que contendrá esas
dos variables
CREATE OR REPLACE PACKAGE pck_profesores AS
v_DNI_profesor profesor.DNI%TYPE;
v_nombre_profesor varchar2(50);
END;
Tablas Mutantes: solución
CREATE OR REPLACE TRIGGER trigger_asignaturas
BEFORE INSERT OR UPDATE ON asignaturas
FOR EACH ROW
BEGIN
IF :NEW.DNI IS NOT NULL THEN
BEGIN
pck_profesores.v_DNI_profesor := :NEW.DNI;
SELECT nombre||' '||apellidos
INTO pck_profesores.v_nombre_profesor
FROM profesores
WHERE DNI = pck_profesores.DNI;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20001,
‘Datos de Profesor erroneos');
END;
END IF;
END;
En el cuerpo del trigger a nivel de fila usamos las variables del
paquete para guardar el DNI y el nombre del profesor
Tablas Mutantes: solución
TRIGGER a nivel de sentencia
Ahora realizamos la consulta utilizando las variables globales
CREATE OR REPLACE TRIGGER trigger_asignaturas_statement
AFTER INSERT OR UPDATE ON asignaturas
DECLARE
v_total INTEGER;
BEGIN
SELECT COUNT(*) INTO v_total
FROM asignaturas
WHERE DNI = pck_profesores.v_DNI_profesor;
-- comprobamos si el profesor aludido está sobrecargado
IF v_total >= 10 THEN
RAISE_APPLICATION_ERROR (-20000, 'El profesor, '||
pck_profesores.v_nombre_profesor || ', está sobrecargado');
END IF;
END;
Tablas Mutantes: solución
UPDATE asignaturas
SET DNI = ‘000000000’
WHERE asignaturas_id = ‘BD’;
UPDATE asignaturas
*
ERROR at line 1:
ORA-20000: El profesor Carlos Romero está sobrecargado
ORA-06512: at "BD_XX.TRIGGER_ASIGNATURAS_STATEMENT", line 11
ORA-04088: error during execution of trigger
'BD_XX.TRIGGER_ASIGNATURAS_STATEMENT'
TRANSACCIONES y TRIGGERS
Los cambios hechos en un TRIGGER deben ser
comprometidos o deshechos con la transacción en la que
se ejecutan
SQL> CREATE TABLE tab1 (col1 NUMBER);
Tabla creada.
SQL> CREATE TABLE log
(timestamp DATE,
operacion VARCHAR2(2000));
Tabla creada.
SQL> CREATE TRIGGER tab1_trig
2 AFTER insert ON tab1
3 BEGIN
4 INSERT INTO log VALUES
(SYSDATE, 'Insert en TAB1');
5 COMMIT;
6 END;
7 /
Trigger created.
SQL> INSERT INTO tab1 VALUES (1);
INSERT INTO tab1 VALUES (1)
*
ERROR at line 1:
ORA-04092: cannot COMMIT in a trigger
ORA-06512: at “BD_XX.TAB1_TRIG", line 3
ORA-04088: error during execution of trigger
‘BD_XX.TAB1_TRIG'
TRANSACCIONES y TRIGGERS
Se pueden utilizar autonomous transactions de manera
que el TRIGGER se ejecute en su propia transacción
SQL> CREATE OR REPLACE TRIGGER tab1_trig
2 AFTER insert ON tab1
3 DECLARE
4 PRAGMA AUTONOMOUS_TRANSACTION;
5 BEGIN
6 INSERT INTO log VALUES
(SYSDATE, 'Insert on TAB1');
7 COMMIT;
8 END;
9 /
Trigger created.
SQL> INSERT INTO tab1 VALUES (1);
1 row created.
TRANSACCIONES AUTÓNOMAS
Se pueden utilizar autonomous transactions de manera
que el TRIGGER se ejecute en su propia transacción
CREATE OR REPLACE PROCEDURE Grabar_Log(descripcion VARCHAR2)
IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO LOG_APLICACION (CO_ERROR, DESCRIPICION, FX_ERROR) VALUES
(SQ_ERROR.NEXTVAL, descripcion, SYSDATE);
COMMIT; -- Este commit solo afecta a la transaccion autonoma
END ;
-- utilizamos el procedimiento desde un bloque PL/SQL
DECLARE
producto PRECIOS%TYPE;
BEGIN
producto := '100599';
INSERT INTO PRECIOS (CO_PRODUCTO, PRECIO, FX_ALTA) VALUES (producto, 150, SYSDATE);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
Grabar_Log(SQLERRM);
ROLLBACK;
-- Los datos grabados por "Grabar_Log" se escriben en la base de datos a pesar del
-- ROLLBACK, ya que el procedimiento está marcado como transacción autonoma.
END;
Ejemplos
Dada la siguiente relación:
SOCIO (num_soc, nombre, direccion, telefono)
Se desea mantener la información de los socios aunque
estos se den de baja, para lo que se crea una tabla
SOCIO_BAJA, que contiene los datos de socio y la
fecha de baja y que se actualizará cada vez que se
borre un socio
SOCIO_BAJA (num_soc, nombre, direccion, telefono, fecha_baja)
Ejemplos
Dadas las siguientes relaciones:
PRODUCTO (cod_prod, descripción, proveedor,
unid_vendidas)
ALMACEN (cod_prod_s, stock, stock_min, stock_max)
1. Se desea mantener actualizado el stock del ALMACEN cada
vez que se vendan unidades de un determinado producto
2. Cuando el stock esté por debajo del mínimo lanzar un mensaje
de petición de compra. Se indicará el número de unidades a
comprar, según el stock actual y el stock maximo
3. Si el stock es menor que el mínimo stock permitido, impedir la
venta
Ejemplos
Dadas las siguientes relaciones:
PROFESOR (cod_prof)
CLASE (cod_clase, cod_prof)
Se define la siguiente vista:
CREATE VIEW informe_profesores AS
SELECT p.cod_prof, COUNT(c.cod_clase) total_clases
FROM profesor p, clase c
WHERE p.cod_prof = c.cod_prof (+)
GROUP BY p.cod_prof;
Se desea poder invocar sentencias del tipo:
DELETE FROM informe_profesores
WHERE cod_prof = 109;
Disparadores en ORACLE
BibliografíaOracle® Database Concepts - 10g Release 2 (10.2) (Octubre 2005)
Oracle® Database Application Developer's Guide – Fundamentals 10g Release 2 (10.2) (Noviembre 2005)
Óscar Díaz – Universidad del País Vasco (UPV)Bases de Datos Activas
top related