Déclencheurs Bases de Données TRIGGER C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI
Généralités Un déclencheur est un traitement (sous forme de bloc PL/SQL) qui s exécute automatiquement en réponse à un événement. Deux types : Déclencheur base de données Déclencheur d application C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 2
Déclencheur Base de Données Exemple Application SQL> INSERT INTO EMP 2...;.; EMP table EMPNO ENAME 7838 KING 7698 BLAKE 7369 SMITH 7788 SCOTT JOB PRESIDENT MANAGER CLERK ANALYST SAL 5000 2850 800 3000 CHECK_SAL trigger C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 3
Création d un déclencheur Synchronisation : BEFORE ou AFTER ou INSTEAD OF Événement : INSERT ou UPDATE ou DELETE Nom Table : On table Type: Ligne(Row) ou ordre SQL Condition restrictive : Clause When Traitement associé : Bloc PL/SQL C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 4
Élément d un déclencheur Synchronisation : BEFORE : Le traitement est exécuté avant l ordre LMD qui l a déclenché. AFTER : Le traitement est exécuté après l ordre LMD qui l a déclenché. C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 5
Élément d un déclencheur Synchronisation : INSTEAD OF: Le traitement est exécuté en lieu et place de l exécution de l ordre LMD qui l a déclenché. Utilisée pour faire des mises à jour "à travers" des VUES. C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 6
Élément d un déclencheur Événement: Indique quel ordre SQL déclenche le traitement : INSERT UPDATE DELETE Toute combinaison de ces ordres C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 7
Élément d un déclencheur Type : Combien de fois le traitement doit s'exécuter suite à l'événement qui l'a déclenché? ORDRE : Le traitement est exécuté une seule fois. Option par défaut. Ligne (Row) : Le traitement est exécuté pour chaque ligne affectée par l'événement. C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 8
Élément d un déclencheur Traitement - corps du déclencheur d : Quelle action à exécuter? Le corps du déclencheur est défini sous forme d'un bloc PL/SQL anonyme. C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 9
Déclencheur ordre et ligne Example SQL> UPDATE emp 2 SET sal = sal * 1.1 3 WHERE deptno = 30; C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 10
Synchronisation du déclencheur BEFORE ordre EMPNO 7839 7698 7788 ENAME KING BLAKE SMITH DEPTNO 30 30 30 BEFORE ligne AFTER ligne BEFORE ligne AFTER ligne BEFORE ligne AFTER ligne AFTER ordre C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 11
Création d'un déclencheur ORDRE CREATE [OR REPLACE] TRIGGER nom_déclencheur positionnement événement [OR événement OR OR...] ON ON Nom_table PL/SQL block; C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 12
Exemple : Déclencheur BEFORE SQL> CREATE OR OR REPLACE TRIGGER secure_emp 2 BEFORE INSERT ON ON emp 3 BEGIN 4 IF IF (TO_CHAR (sysdate,'dy') IN IN ('SAT','SUN')) 5 OR OR (TO_CHAR(sysdate,'HH24')NOT BETWEEN 6 '08' AND '18' 7 THEN RAISE_APPLICATION_ERROR (-20500, 8 'Vous ne ne pouvez utiliser la la table EMP que pendant les heures normales.'); 10 10 END IF; 11 11 END; 12 12 / C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 13
Exemple (suite) SQL> INSERT INTO emp (empno, ename, deptno) 2 VALUES (7777, 'BAUWENS', 40); INSERT INTO emp (empno, ename, deptno) * ERROR at at line 1: 1: ORA-20500: 'Vous ne ne pouvez utiliser la la table EMP que pendant les heures normales. ORA-06512: at at "SCOTT.SECURE_EMP", line 4 ORA-04088: error during execution of of trigger 'SCOTT.SECURE_EMP' C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 14
Conditions sur l'événement SQL>CREATE OR OR REPLACE TRIGGER secure_emp 2 BEFORE INSERT OR OR UPDATE OR OR DELETE ON ON emp emp 3 BEGIN 4 IF IF (TO_CHAR (sysdate,'dy') IN IN ('SAT','SUN')) OR OR 5 (TO_CHAR (sysdate, 'HH24') NOT NOT BETWEEN '08' AND AND '18') THEN 6 IF IF DELETING THEN 7 RAISE_APPLICATION_ERROR (-20502, 8 'Suppression impossible à cette heure.'); 9 ELSIF INSERTING THEN 10 10 RAISE_APPLICATION_ERROR (-20500, 11 11 'Création impossible à cette heure.'); 12 12 ELSIF UPDATING ('SAL') THEN 13 13 RAISE_APPLICATION_ERROR (-20503, 14 14 'Modification impossible à cette heure.'); 15 15 ELSE ELSE 16 16 RAISE_APPLICATION_ERROR (-20504, 17 17 'Mises à jour jour impossibles à cette heure.'); 18 18 END END IF; IF; 19 19 END END IF; IF; 20 20 END; C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 15
Exemple : Déclencheur AFTER SQL>CREATE OR OR REPLACE TRIGGER check_salary_count 2 AFTER UPDATE OF OF sal sal ON ON emp emp 3 DECLARE 4 v_salary_changes NUMBER; 5 v_max_changes NUMBER; 6 BEGIN 7 SELECT upd, upd, max_upd 8 INTO v_salary_changes, v_max_changes 9 FROM audit_table 10 10 WHERE user_name = user user 11 11 AND AND table_name = 'EMP' 12 12 AND AND column_name = 'SAL'; 13 13 IF IF v_salary_changes > v_max_changes THEN THEN 14 14 RAISE_APPLICATION_ERROR (-20501, 15 15 'Respectez le le maximum : ' ' 16 16 TO_CHAR (v_max_changes) 17 17 ' admissible pour pour le le salaire'); 18 18 END END IF; IF; 19 19 END; C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 16
Utilisation d'une table d'audit USER_NAME TABLENAME COLUMN_NAME INS UPD DEL SCOTT EMP 1 1 1 SCOTT EMP SAL 1 JONES EMP 0 0 0... MAX_INS MAX_UPD MAX_DEL 5 5 5 5 5 0 0 C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 17
Création d'un déclencheur LIGNE CREATE [OR REPLACE] TRIGGER nom_déclencheur positionnement événement [OR événement OR OR...] ON ON nom_table [REFERENCING OLD AS AS old NEW AS AS new] FOR EACH ROW [WHEN condition] PL/SQL block; OLD : avant exécution de de l'ordre LMD NEW : après exécution de de l'ordre LMD C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 18
Déclencheur AFTER : Exemple SQL>CREATE OR OR REPLACE REPLACE TRIGGER TRIGGER audit_emp 2 AFTER AFTER DELETE DELETE OR OR INSERT INSERT OR OR UPDATE UPDATE ON ON emp emp 3 FOR FOR EACH EACH ROW ROW 4 BEGIN BEGIN 5 IF IF DELETING DELETING THEN THEN 6 UPDATE UPDATE audit_table SET SET del del = del del + 1 7 WHERE WHERE user_name = user user AND AND table_name = 'EMP' 'EMP' 8 AND AND column_name IS IS NULL; NULL; 9 ELSIF ELSIF INSERTING THEN THEN 10 10 UPDATE UPDATE audit_table SET SET ins ins = ins ins + 1 11 11 WHERE WHERE user_name = user user AND AND table_name = 'EMP' 'EMP' 12 12 AND AND column_name IS IS NULL; NULL; 13 13 ELSIF ELSIF UPDATING UPDATING ('SAL') ('SAL') THEN THEN 14 14 UPDATE UPDATE audit_table SET SET upd upd = upd upd + 1 15 15 WHERE WHERE user_name = user user AND AND table_name = 'EMP' 'EMP' 16 16 AND AND column_name = 'SAL'; 'SAL'; 17 17 ELSE ELSE /* /* The The data data manipulation operation is is a general general UPDATE. UPDATE. */ */ 18 18 UPDATE UPDATE audit_table SET SET upd upd = upd upd + 1 19 19 WHERE WHERE user_name = user user AND AND table_name = 'EMP' 'EMP' 20 20 AND AND column_name IS IS NULL; NULL; 21 21 END END IF; IF; 22 22 END; END; C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 19
Utilisation des valeurs avant et après SQL>CREATE OR OR REPLACE TRIGGER audit_emp_values 2 AFTER DELETE OR OR INSERT OR OR UPDATE ON ON emp 3 FOR EACH ROW 4 BEGIN 5 INSERT INTO audit_emp_values (user_name, 6 timestamp, id, old_last_name, new_last_name, 7 old_title, new_title, old_salary, new_salary) 8 VALUES (USER, SYSDATE, :old.empno, :old.ename, 9 :new.ename, :old.job, :new.job, 10 10 :old.sal, :new.sal); 11 11 END; 12 12 / C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 20
Utilisation de la table Audit_Emp_Values USER_NAME EGRAVINA TIMESTAMP 12-NOV-97 ID 7950 OLD_LAST_NAME NULL NEW_LAST_NAME HUTTON NGREENBE 10-DEC-97 7844 MAGEE TURNER... OLD_TITLE NULL NEW_TITLE ANALYST OLD_SALARY NULL NEW_SALARY 3500 CLERK SALESMAN 1100 1100 C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 21
Restriction déclencheur LIGNE Clause WHEN SQL>CREATE OR OR REPLACE TRIGGER derive_commission_pct 2 BEFORE INSERT OR OR UPDATE OF OF sal ON ON emp 3 FOR EACH ROW 4 WHEN (new.job = 'SALESMAN') 5 BEGIN 6 IF IF INSERTING THEN :new.comm := := 0; 0; 7 ELSE /* /* UPDATE of of salary */ */ 8 IF IF :old.comm IS IS NULL THEN 9 :new.comm :=0; 10 10 ELSE 11 11 :new.comm := := :old.comm * (:new.sal/:old.sal); 12 12 END IF; 13 13 END IF; 14 14 END; 15 15 / C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 22
Déclencheur ou Procédure Stockée Déclencheur CREATE TRIGGER Code source et p-code dans le dictionnaire de la base Appel implicite COMMIT, SAVEPOINT, ROLLBACK non autorisés Procédure CREATE PROCEDURE Code source et p-code dans le dictionnaire de la base Appel explicite COMMIT, SAVEPOINT, ROLLBACK autorisés C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 23
Gestion des Déclencheurs Activation / désactivation d d'un seul déclencheurd ALTER TRIGGER nom_déclencheur ALTER TABLE nom_table DISABLE ENABLE DISABLE ENABLE; Activation / désactivation d de tous les déclencheurs associés à une table Recompilation d un déclencheurd ALTER TRIGGER trigger_name COMPILE; ALL TRIGGERS; C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 24
Suppression d'un Déclencheur DROP TRIGGER : DROP TRIGGER nom_déclencheur; C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 25
Règles d'utilisation des Déclencheurs Ligne Règle 1: Ne pas modifier les données des colonnes définies comme : primary key, foreign key, ou unique key d une table liée. Règle 2: Ne pas accéder aux données d'une table en mise à jour («mutating» table). C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 26
Déclencheur et Tables Liées (1) évènement Traitement SQL> UPDATE dept 2 SET deptno = 1 3 WHERE deptno = 30; EMP table EMPNO ENAME 7698 7654 7499 Table liée BLAKE MARTIN ALLEN Intégrit grité référentiellerentielle DEPTNO DEPTNO 30 10 30 20 30 30 40 Erreur AFTER UPDATE ligne DEPT table DNAME ACCOUNTING RESEARCH SALES OPERATIONS xxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxx vvvvvvvvvvvvvvvvvvvvvvvvvvvv vvvvvvvvvvvvvvvvvvvvvvvvvvvv xxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxx vvvvvvvvvvvvvvvvvvvvvvvvvvvv vvvvvvvvvvvvvvvvvvvvvvvvvvvv xxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxx Déclencheurs en cascade C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 27
Déclencheur et Tables Liées (2) : Déclencheur sur table DEPT SQL>CREATE OR OR REPLACE TRIGGER cascade_updates 2 AFTER UPDATE OF OF deptno on on DEPT 3 FOR EACH ROW 4 BEGIN 5 UPDATE emp 6 SET emp.deptno = :new.deptno 7 WHERE emp.deptno = :old.deptno; 8 END; 9 / C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 28
Déclencheur et Tables Liées (3) : Action sur table DEPT SQL> UPDATE dept 2 SET deptno = 1 3 WHERE deptno = 30; * ERROR at at line 1: 1: ORA-04091: table DEPT is is mutating, trigger/function may not see it it C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 29
Consultation de données sur une table en mise à jour («Mutating») SQL> UPDATE emp 2 SET sal = 1500 3 WHERE ename = 'SMITH'; EMP table EMPNO ENAME 7369 7698 7788 SMITH BLAKE SCOTT Table en mise à jour JOB CLERK MANAGER ANALYST SAL 1500 2850 3000 évènement ERREUR BEFORE UPDATE ligne CHECK_SALARY trigger C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 30
Table en mise à jour : exemple - 1 SQL>CREATE OR OR REPLACE TRIGGER check_salary 2 BEFORE INSERT OR OR UPDATE OF OF sal, job ON ON emp 3 FOR EACH ROW 4 WHEN (new.job <> <> 'PRESIDENT') 5 DECLARE 6 v_minsalary emp.sal%type; 7 v_maxsalary emp.sal%type; C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 31
Table en mise à jour : exemple - suite 8 BEGIN 9 SELECT MIN(sal), MAX(sal) 10 10 INTO v_minsalary, v_maxsalary 11 11 FROM emp 12 12 WHERE job = :new.job; 13 13 IF IF :new.sal < v_minsalary OR OR 14 14 :new.sal > v_maxsalary THEN 15 15 RAISE_APPLICATION_ERROR(-20505, 16 16 'salaire hors normes'); 17 17 END IF; 18 18 END; 19 19 / C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 32
Table en mise à jour : exemple - suite SQL> UPDATE emp 2 SET sal = 1500 3 WHERE ename = 'SMITH'; * ERROR at at line 2 ORA_4091 : Table EMP is is mutating, trigger/function may not see it it ORA_06512: at at line 4 ORA_04088: error during execution of of trigger 'check_salary' C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 33
Utilisation des Déclencheurs Sécurité Audit Intégrité des données Intégrité référentielle Réplication de données Données dérivées Génération d'événements C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 34
Exemple : Intégrité des données SQL>CREATE OR OR REPLACE TRIGGER check_salary 2 BEFORE UPDATE OF OF sal ON ON emp 3 FOR EACH ROW 4 WHEN (new.sal < old.sal) OR OR 5 (new.sal > old.sal * 1.1) 6 BEGIN 7 RAISE_APPLICATION_ERROR (-20508, 8 'Il ne ne faut pas diminuer le le salaire ni ni l'augmenter de de plus de de 10%.'); 10 10 END; 11 11 / C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 35
Exemple : Intégrité référentielle SQL>CREATE OR OR REPLACE TRIGGER cascade_updates 2 AFTER UPDATE OF OF deptno ON ON dept 3 FOR EACH ROW 4 BEGIN 5 UPDATE emp 6 SET emp.deptno = :new.deptno 7 WHERE emp.deptno = :old.deptno; 8 END; 9 / C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 36
Exemple : Réplication de données SQL>CREATE OR OR REPLACE TRIGGER emp_replica 2 BEFORE INSERT OR OR UPDATE ON ON emp emp 3 FOR FOR EACH ROW ROW 4 BEGIN /*Only proceed if if user init. data operation, 5 NOT NOT the the casc. trigger.*/ 6 IF IF INSERTING THEN 7 IF IF :new.flag IS IS NULL THEN 8 INSERT INTO emp@sf VALUES (:new.empno, 9 :new.ename,...,'b'); 10 10 :new.flag = 'A'; 'A'; 11 11 END END IF; IF; 12 12 ELSE /* /* Updating. */ */ 13 13 IF IF :new.flag = :old.flag THEN 14 14 UPDATE emp@sf SET SET ename = :new.ename,..., 15 15 FLAG = :new.flag 16 16 WHERE empno = :new.empno; 17 17 END END IF; IF; 18 18 IF IF :old.flag = 'A' 'A' THEN THEN :new.flag := := 'B'; 'B'; 19 19 ELSE :new.flag := := 'A'; 20 20 END END IF; IF; 21 21 END END IF; IF; 22 22 END; 23 23 / C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 37
Exemple : données dérivées SQL>CREATE OR OR REPLACE PROCEDURE increment_salary 2 (v_id IN IN dept.deptno%type, 3 v_salary IN IN dept.total_salary%type) 4 IS IS 5 BEGIN 6 UPDATE dept dept 7 SET SET total_sal = NVL NVL (total_sal,0)+ v_salary 8 WHERE deptno = v_id; 9 END END increment_salary; 10 10 / SQL>CREATE OR OR REPLACE TRIGGER compute_salary 2 AFTER INSERT OR OR UPDATE OF OF sal sal OR OR DELETE ON ON emp emp 3 FOR FOR EACH ROW ROW 4 BEGIN 5 IF IF DELETING THEN increment_salary(:old.deptno, -1-1 * :old.sal); 6 ELSIF UPDATING THEN increment_salary(:new.deptno, 7 :new.sal-:old.sal); 8 ELSE /*inserting*/ increment_salary(:new.deptno, :new.sal); 9 END END IF; IF; 10 10 END; 11 11 / C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 38
Exemple : Génération d'événements SQL>CREATE OR OR REPLACE REPLACE TRIGGER TRIGGER notify_reorder_rep 2 AFTER AFTER UPDATE UPDATE OF OF amount_in_stock, reorder_point ON ON inventory 3 FOR FOR EACH EACH ROW ROW 4 WHEN WHEN new.amount_in_stock <= <= new.reorder_point 5 DECLARE DECLARE 6 v_descrip product.descrip%type; 7 v_msg_text VARCHAR2(2000); 8 BEGIN BEGIN 9 SELECT SELECT descrip descrip INTO INTO v_descrip 10 10 FROM FROM PRODUCT PRODUCT WHERE WHERE prodid prodid = :new.product_id; 11 11 v_msg_text := := 'It 'It has has come come to to my my personal personal attention that, that, 12 12 due due to to recent recent ' 13 13 CHR(10) CHR(10) 'transactions, our our inventory for for product product # ' ' 14 14 TO_CHAR(:new.product_id) '--' 15 15 :new.product_name '-- '-- has has fallen' fallen' CHR(10) CHR(10) CHR(10) CHR(10) 16 16 'Yours,' 'Yours,' CHR(10) user user '.'; '.'; 17 17 dbms_mail.send ('Inventory', user,null,null,'low 18 18 Inventory',null,v_msg_text); 19 19 END; END; 20 20 / C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 39
Procédure Résumé Package Déclencheur xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx vvvvvvvvvvvvvvvvvv vvvvvvvvvvvvvvvvvv xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx vvvvvvvvvvvvvvvvvv vvvvvvvvvvvvvvvvvv xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx vvvvvvvvvvvvvvvvvv vvvvvvvvvvvvvvvvvv xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx vvvvvvvvvvvvvvvvvv vvvvvvvvvvvvvvvvvv xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx vvvvvvvvvvvvvvvvvv vvvvvvvvvvvvvvvvvv xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx vvvvvvvvvvvvvvvvvv vvvvvvvvvvvvvvvvvv xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx Procédure A déclaration Procédure B définition Procédure A définition variable locale C. Bonnet // R. Chapuis Cours PL/SQL d après cours ORACLE - OAI / 40