Diapositiva 1

Anuncio
Disparadores en ORACLE
Bibliografía
Oracle® 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
DECLARE ▬ optional
BEGIN ▬ required
EXCEPTION ▬ optional
 Soportan DML y DDL
 Anónimos / Con nombre → TRIGGERS
END;
/
required
Disparadores (triggers) en Oracle: bloques de código
que son implícitamente invocados cuando algo sucede
CREATE TRIGGER
Triggers VS Procedimientos
NombreTrigger
 Ejecución Implícita: Disparar
 No admiten argumentos
Aplicaciones:
 Restricciones (Constraints)
 Auditorías
 Informar de Eventos
BEFORE INSERT ON StarsIn
DECLARE
…..
END;
TRIGGER
/
CREATE PROCEDURE Get_emp_rec
(Emp_number IN
Emp_tab.Empno%TYPE) AS
BEGIN
- - - - END;
/
PROCEDIMIENTO
Introducción
3 Tipos
 DML/DDL (Fila/Sentencia, BEFORE/AFTER)
 INSTEAD OF
 SYSTEM
Estructura General de un Disparador
Evento
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]
Condición
BEGIN
cuerpo del trigger
END;
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 sentencia
CREATE TRIGGER NombreTrigger
BEFORE Insert ON NombreTabla ….
Los de tipo BEFORE son
mejores en términos de
rendimiento
AFTER
 Ejecutan la acción asociada después de que
se haya ejecutado la sentencia
CREATE TRIGGER NombreTrigger
AFTER Insert ON NombreTabla ….
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 dispara
CREATE 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 sentencia
CREATE 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.
2.
3.
Triggers Before (nivel de sentencia)
Para cada fila:
1. Trigger Before (a nivel de fila)
2. Ejecuta la Sentencia
3. Triggers After (a nivel de fila)
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_Docencia
AFTER DELETE ON Profesores
WHEN old.nombre LIKE ‘%Juan%’
FOR EACH ROW
DECLARE
var VARCHAR2(50);
BEGIN
SELECT a.nombre INTO var FROM Asignaturas a
WHERE REF(a) = :NEW.asignatura;
DELETE FROM Asignaturas WHERE nombre = var
END;
/
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)
SINTAXIS


Condición (WHEN ….)
En el cuerpo del disparador
OLD, NEW
:OLD, :NEW
ROW TRIGGER: ejemplo
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
Persona
Cod Nombre Edad
C1
C2
C3
C4
C5
María
Pepe
Pepe
Luisa
Pepe
25
40
45
48
22
DELETE FROM persona
WHERE cod in (‘C1’,‘C3’,‘C4’)
Persona2
Cod
C1
C2
C3
C4
C5
Nombre Edad
María
Pepe
Pepe
Luisa
Pepe
25
40
45
48
22
Borra C3 y C4
de persona2
STATEMENT TRIGGER: ejemplo
Cuando se borre en la tabla socio emitir un mensaje
indicando que no se pueden borrar socios
Socio
Cod Nombre Fecha_ant
S1
S2
S3
S4
S5
María
Pepe
Pepe
Luisa
Pepe
DELETE FROM socio
WHERE nombre = ‘Pepe’
......
......
......
......
......
Borra 3 tuplas y se emite
un único mensaje
Triggers DML
Disparados por sentencias DML:
 INSERT, UPDATE o DELETE
Todas las filas o sólo algunas (WHEN)
LIBROS
ESTADÍSTICAS
ISBN
GENERO
TÍTULO
GENERO
TOTAL_LIBROS
100-09-89
Novela
El Quijote
Novela
50
-----
----
----
Infantil
15
CREATE OR REPLACE TRIGGER
UpdateEstadisticasGenero
AFTER INSERT OR DELETE OR UPDATE ON Libros
DECLARE
UDDATE Estadisticas SET ….
BEGIN
---------------------END UpdateEstadisticasGenero;
/
Triggers INSTEAD OF
Sólo sobre VISTAS
EMPLEADO
DNI
DEPARTAMENTO
NOMBRE
DEPARTAMENT
O
11111111
José García
CT-1
-----
----
----
NOMBRE
CÓDIGO
Contabilidad - 1
CT-1
Recursos
Humanos
RRHH
CREATE VIEW EmpleadoDpto as
SELECT e.nombre, d.nombre FROM Empleado E, Departamento D
WHERE E.Departamento = D.Codigo;
CREATE OR REPLACE TRIGGER InsertEmepleadoDpto
INSERT INTO EmpleadoDpato
VALUES (‘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
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
Inserting, Deleting, Updating
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;
/
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
CREATE...
BEFORE...
[FOR EACH ROW ...]
DECLARE
Declaración de
variables
BEGIN
nombre
nombre
nombre
nombre
CONSTANT NUMBER:=valor;
TIPO;
nombretabla.nombrecolumna%TYPE;
nombretabla%ROWTYPE
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 disparador
SELECT TRIGGER_BODY
FROM USER_TRIGGERS
WHERE TRIGGER_NAME='nombre_disparador';
Ver la descripción de un disparador
SELECT DESCRIPTION
FROM USER_TRIGGERS
WHERE TRIGGER_NAME= 'nombre_disparador';
Ejemplo
SELECT Trigger_body FROM USER_TRIGGERS
CREATE TRIGGER Ejemplo
WHERE Trigger_name = 'Ejemplo';
AFTER DELETE ON tabla1
FOR EACH ROW
TRIGGER_BODY
WHEN ((OLD.nombre=’pepe’)
OR (OLD.edad > 35))
----------------------------------
BEGIN
DELETE FROM tabla2
WHERE tabla2.cod=:OLD.cod;
BEGIN
DELETE FROM tabla2 WHERE
tabla2.cod=:OLD.cod;
END Ejemplo;
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
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:
SELECT COUNT(*)
INTO v_total
FROM asignaturas
WHERE DNI = :NEW.DNI;
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'
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
 En el cuerpo del trigger a nivel de fila usamos las variables del
paquete para guardar el DNI y el nombre del profesor
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;
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> INSERT INTO tab1 VALUES (1);
INSERT INTO tab1 VALUES (1)
*
ERROR at line 1:
SQL> CREATE TRIGGER tab1_trig
2
3
AFTER insert ON tab1
BEGIN
4
INSERT INTO log VALUES
(SYSDATE, 'Insert en TAB1');
5
COMMIT;
6
END;
7
/
Trigger created.
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
3
AFTER insert ON tab1
DECLARE
4
5
PRAGMA AUTONOMOUS_TRANSACTION;
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.
2.
3.
Se desea mantener actualizado el stock del ALMACEN cada
vez que se vendan unidades de un determinado producto
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
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ía
Oracle® 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
Descargar