Trigger Trigger Un trigger definisce un’azione che il database deve attivare automaticamente quando si verifica nel database un determinato evento. possono essere utilizzati: per migliorare l’integrità referenziale dichiarativa per imporre regole complesse legate all’attività del database per effettuare revisioni sulle modifiche dei dati. Trigger L’esecuzione dei trigger è quindi trasparente all’utente. I trigger vengono eseguiti automaticamente dal database quando specifici tipi di comandi (Eventi) di manipolazione dei dati vengono eseguiti su specifiche tabelle. Tali comandi comprendono i comandi DML insert, update e delete, ma gli ultimi DBMS prevedono anche trigger su istruzioni DDL come Create View etc. Anche gli aggiornamenti di specifiche colonne possono essere utilizzati come trigger di eventi. Privilegi Per creare un trigger su una tabella si deve avere la possibilità di modificare la tabella. Per cui bisogna essere prima di tutto proprietari della tabella, e poi bisogna avere il privilegio ALTER per la tabella o il privilegio di sistema ALTER ANY TABLE. Inoltre occorre avere il privilegio di sistema CREATE TRIGGER. Analogamente, per potere modificare un trigger di una tabella, si deve essere proprietari della tabella e inoltre disporre dei privilegi ALTER ANY TRIGGER. Trigger a livello di riga I trigger a livello di riga vengono eseguiti una volta per ciascuna riga modificata in una transazione; vengono spesso utilizzati in applicazioni di revisione dei dati e si rivelano utili per operazioni di audit dei dati e per mantenere sincronizzati i dati distribuiti. Per creare un trigger a livello di riga occorre specificare la clausola FOR EACH ROW nell’istruzione create trigger. Trigger a livello di istruzione I trigger a livello di istruzione vengono eseguiti una sola volta per ciascuna transazione, indipendentemente dal numero di righe che vengono modificate. Vengono pertanto utilizzati per attività correlate ai dati; vengono utilizzati di solito per imporre misure aggiuntive di sicurezza sui tipi di transazione che possono essere eseguiti su una tabella. E’ il tipo di trigger predefinito nel comando create trigger (ossia non occorre specificare che è un trigger al livello di istruzione). Tipi di Trigger BEFORE e AFTER: i trigger possono essere eseguiti prima o dopo l’utilizzo dei comandi insert, update e delete; all’interno del trigger è possibile fare riferimento ai vecchi e nuovi valori coinvolti nella transazione. Occorre utilizzare la clausola BEFORE/AFTER <tipo di evento> (insert, delete, update). Tipi di Trigger INSTEAD OF: per specificare che cosa fare invece di eseguire le azioni che hanno attivato il trigger. Ad esempio, è possibile utilizzare un trigger INSTEAD OF per reindirizzare le insert in una tabella verso una tabella differente o per aggiornare con update più tabelle che siano parte di una vista. Tipi di Trigger BEFORE INSERT riga BEFORE INSERT istruzione AFTER INSERT riga AFTER INSERT istruzione BEFORE UPDATE riga BEFORE UPDATE istruzione AFTER UPDATE riga AFTER UPDATE istruzione BEFORE DELETE riga BEFORE DELETE istruzione AFTER DELETE riga AFTER DELETE istruzione INSTEAD OF riga INSTEAD OF istruzione Trigger, struttura • L’istruzione Create seguita dal nome assegnato al trigger • Tipo di trigger, Before/After • Evento che scatena il trigger Insert/Delete/Update • [For each row], Se si vuole specificare trigger al livello di riga (altrimenti nulla per trigger al livello di istruzione) • Specificare a quale tabella si applica • Condizione che si deve verificare perchè il trigger sia eseguito • Azione, definita dal codice da eseguire se si verifica la condizione Trigger: sintassi create trigger <NomeTrigger> Modo Evento {, Evento} ON <TabellaTarget> for each row [when <Predicato SQL>] Blocco PL/SQL Modo Evento: before o after Evento: insert, update, delete for each row specifica la granularità. In assenza di questa clausola si intende per ogni istruzione Vecchi e nuovi valori Poiché la maggior parte dei trigger ha a che fare con modifiche di righe, è importante poter fare riferimento ai valori della riga prima dell’inserimento, cancellazione, modifica e i valori dopo tali eventi. In particolare se, per esempio, si tratta di un trigger BEFORE UPDATE, per valori vecchi intendiamo i valori che sono nella tabella e che vogliamo modificare e per nuovi quelli che vogliamo inserire al posto dei vecchi. Se viceversa è un trigger AFTER UPDATE, per vecchi intendiamo quelli che c’erano prima dell’update e nuovi quelli presenti nella tabella alla fine della modifica. Nuovi e vecchi valori In Oracle ci si può riferire automaticamente ai vecchi valori (ossia i valori prima dell’aggiornamento) e ai nuovi valori (ossia quelli dopo l’aggiornamento) rispettivamente mediante le parole chiave “old” e “new”. Mentre nella condizione (when) le parole chiave old e new compaiono senza nessun altro simbolo, nell’azione le parole chiave old e new sono precedute da due punti (:old, :new) Esempio Costruire il trigger TrigBirra che inserisce un nome di birra in una tabella Birre quando viene inserita nella tabella Vendite una birra che non era già presente nella tabella Birre. Birre Nome Vendite Birra Cod_bar prezzo Esempio Birre Nome Vendite Birra Cod_bar prezzo CREATE OR REPLACE TRIGGER TrigBirra AFTER INSERT ON Vendite Evento FOR EACH ROW WHEN (new.birra NOT IN Condizione (SELECT nome FROM Birre)) BEGIN INSERT INTO BIRRE(nome) VALUES(:new.birra); Azione END Esempio Creare il trigger TrigPrezzo che memorizza nella relazione RipoffBars(cod_bar) il nome di ogni bar che aumenta il prezzo di una qualunque birra di più di 1 euro. ListaBar Cod_bar Nome_bar Indirizzo_bar Telefono_bar Vendite Birra Cod_bar prezzo RipoffBar Cod_bar ListaBar Cod_bar Nome_bar Indirizzo_bar Telefono_bar Vendite Birra Cod_bar prezzo RipoffBar Cod_bar CREATE OR REPLACE TRIGGER TrigPrezzo AFTER UPDATE OF prezzo ON Vendite FOR EACH ROW WHEN (new.prezzo > old.prezzo + 1.00) BEGIN INSERT INTO RipoffBars VALUES(:new.cod_bar); END Esempio Biblioteca Titolo Editore Nome_cat Classificazione Audit_Biblioteca Titolo Editore Nome_cat Vecchia_Class Nuova_class Data_audit Vogliamo che la tabella Audit_Biblioteca tenga traccia di ogni modifica della classificazione della tabella Biblioteca quando il valore di classificazione viene ridotto. Esempio Assegna Nome trigger Prima dell’aggiornamento Create or replace Trigger Biblioteca_bef_upd_Row BEFORE UPDATE on Biblioteca FOR EACH ROW Per ogni riga modificata WHEN (new.Classificazione<old.Classificazione) Quando il nuovo valore della classificazione riduce il precedente BEGIN INSERT INTO Audit_Biblioteca (Titolo, Editore, Nome_Cat, Vecchia_Class, Nuova_Class, Data_Audit) VALUES(:old.Titolo, :old.Editore, :old:Nomecat, :old.Classificazione, :new.Classificazione, Sysdate) END Azione Osservazione Il funzionamento del trigger risulta completamente trasparente all’utente che esegue l’aggiornamento della tabella Biblioteca. Tuttavia la transazione eseguita sulla tabella Biblioteca dipende dal successo dell’esecuzione del trigger Drop Trigger Un trigger si elimina mediante l’istruzione DROP Trigger <nome trigger> Combinazione di tipi di trigger E’ possibile combinare diversi trigger su diversi comandi insert, update e delete, purchè siano tutti allo stesso livello. In tal caso l’evento può essere indicato per esempio come segue Before insert OR update E i diversi casi vengono selezionati mediante i diversi tipi di transazione, che sono INSERTING, DELETING e UPDATING. Esempio Si consideri il trigger Biblioteca_Bef_Upd_Ins_Row, che modifica la tabella Audit_Biblioteca, in cui, se viene fatto un nuovo inserimento nella tabella Biblioteca, allora vengono inseriti nella tabella Audit_Biblioteca i valori del titolo, editore, categoria, nuova classificazione e data dell’inserimento; se viene effettuata una modifica, oltre a questi valori, viene registrata nella tabella Audit_Biblioteca anche la vecchia classificazione. Biblioteca Titolo Editore Nome_cat Classificazione Audit_Biblioteca Titolo Editore Nome_cat Vecchia_Class Nuova_class Data_audit Esempio Create or replace trigger Biblioteca_Bef_Upd_Ins_Row Before Insert or Update of Classificazione on Biblioteca For each Row Begin If INSERTING then Insert into audit_Biblioteca (Titolo, Editore, Nome_cat, Nuova_Class, Data_Audit) Values(:new.Titolo, :new.Editore, :new.Nome_cat, :new.Classificazione, Sysdate); ELSE if UPDATING then Insert into audit_Biblioteca(Titolo, Editore, Nome_cat, Vecchia_Class, Nuova_Class, Data_Audit) Values(:new.Titolo, :new.Editore, :new.Nome_cat, :old.Classificazione, :new.Classificazione, Sysdate); End if; End if; End; Magazzino Prodotto QtaDisp Soglia QtaRiordino Ordini_Pendenti Prodotto Qta Data Creare un trigger che controlla ad ogni modifica della quantità disponibile di un prodotto in magazzino, se questa quantità disponibile è andata al di sotto della soglia. Nel qual caso, se il prodotto non è già presente negli ordini pendenti, viene aggiunto un ordine del prodotto nella tabella OrdiniPendenti, di una quantità uguale alla quantità riordino presente nella tabella Magazzino. Magazzino Prodotto QtaDisp Soglia QtaRiordino Ordini_Pendenti Prodotto Qta Data create trigger Riordina after update of QtaDisp on Magazzino when (new.QtaDisp < new.Soglia) for each row declare X number; begin select count(*) into X from OrdiniPendenti where Prodotto = :new.Prodotto; if X = 0 then insert into OrdiniPendenti values (:new.Prodotto, :new.QtaRiordino, sysdate); end if; end Clienti Cod_cliente Nome Città sconto Ordini Num_ord Cod_cliente Cod_agente Prodotto Data ammontare Supponiamo che si voglia imporre il vincolo che non si accettano nuovi ordini da clienti con uno scoperto maggiore di 10.000 euro. Creare un trigger affinchè si soddisfi questo vincolo Clienti Cod_cliente Nome Città sconto Ordini Num_ord Cod_cliente Cod_agente Prodotto Data Ammontare Create trigger ControlloFido Before insert on Ordini For each row Declare DaPagare Number; BEGIN Select sum(ammontare) into DaPagare Eccezione From Ordini Where cod_cliente=:new.cod_cliente); IF DaPagare+:new.ammontare >=10000 THEN RAISE_APPLICATION_ERROR(20001, ‘Fido Superato’) ; ENDIF; END Trigger Attivi e Passivi Un trigger è attivo quando, in corrispondenza di certi eventi, modifica lo stato della base di dati. Un trigger è passivo se serve a provocare il fallimenti della transazione corrente sotto certe condizioni. Solo l’ultimo esempio fra quelli visti è un trigger passivo, mentre gli altri sono tutti attivi. Trigger Attivi 1. Per definire le cosiddette business rules, ovvero le azioni da eseguire per garantire la corretta evoluzione del sistema informativo 2. Per memorizzare eventi sulla base di dati per ragioni di controllo (auditing e logging) 3. Per propagare su altre tabelle gli effetti di certe operazioni su tabelle 4. Per mantenere allineati eventuali dati duplicati quando si modifica uno di essi Trigger Passivi 1. Per definire vincoli di integrità non esprimibili nel modello dei dati usato 2. Per fare controlli sulle operazioni ammissibili degli utenti basati sui valori dei parametri di comandi SQL Personalizzare le condizioni di errore I numeri e i messaggi di errore visualizzati dall’utente sono impostati mediante la procedura RAISE_APPLICATION_ERROR Che può essere chiamata all’interno di ogni trigger Raise_Application_Error è una procedura che prende in input due parametri Il numero dell’errore (che deve essere un numero compreso tra -20001 e -20999) Il messaggio di errore da visualizzare Esempio Quando un utente cerca di cancellare una riga dalla tabella Biblioteca, il trigger Trigger Biblioteca_Bef_Del deve verificare che l’operazione non sia fatta durante il weekend e che l’username dell’account che effettua l’eliminazione sia ‘LIB’. Il codice del trigger corrispondente è il seguente: Create or Replace Trigger Biblioteca_Bef_Del Before deletion on Biblioteca Declare Weekend_error EXCEPTION; Not_library _user EXCEPTION; Dichiarazione delle eccezioni Si assegna un nome alle eccezioni BEGIN IF to_char(sysdate, ‘DY’)=‘Sab’ or to_char(Sysdate, ‘DY’)=‘Dom’ THEN RAISE Weekend_error; End if; If upper(Utente)<> ‘LIB’ THEN RAISE not_library_error; End if; Definizione di weekend_error EXCEPTION WHEN weekend_error THEN Raise_Application_Error (-20001, ‘Cancellazioni non permesse durante il weekend’); WHEN not_library_user THEN Raise_Application_Error (-20002, ‘Le cancellazioni sono permesse solo agli operatori’); END Definizione di not_library_user Trigger: Esempio Registro Data Azione Articolo Quantità Importo Persona Audit_Registro Data Azione Articolo Quantità Importo Persona Aumento Scrivere un trigger sulla tabella registro che ad ogni modifica della tabella registro per cui l’aumento dell’importo è superiore al 10%, inserisce nella tabella Registro_audit i vecchi valori del dato inserito, più l’aumento dell’importo Esercizio create trigger registro_bef_upd_row before update on REGISTRO for each row when (new.Importo/old.Importo>1.1) begin insert into REGISTRO_AUDIT values (:old.DataAzione, :old.Azione, :old.Articolo, :old.Quantita, :old.TipoQuantita, :old.Tasso, :old.Importo, :old.Persona, :new.Importo – :old_Importo); end; Trigger: esempio Creare un trigger sulla tabella Registro che prima di inserire una riga nella tabella registro, inserisca il nome nella tabella Persone, con tutti i caratteri maiuscoli create trigger registro_bef_upd_ins_row before insert or update of Persona on REGISTRO for each row begin :new.MaiuscPersona := UPPER(:new.Persona); end; Trigger: attivazione e disattivazione Abilita il trigger Biblioteca_Bef_Del alter trigger Biblioteca_Bef_Del enable; Abilita tutti i trigger sulla tabella Biblioteca alter table BIBLIOTECA enable all triggers; alter trigger Biblioteca_Bef_Del disable; alter table BIBLIOTECA disable all triggers; Disabilita il trigger Biblioteca_Bef_Del Disabilita tutti i trigger sulla tabella Biblioteca