1
Les déclencheurs (Triggers) avec Oracle
Sommaire
1) Introduction
2) Événements déclenchant 3) Mécanisme général
4) Privilèges systèmes
5) Création/destruction/manipulation d’un déclencheur 6) Option BEFORE ou AFTER
7) Déclencheur LMD 8) Déclencheur LDD
9) Déclencheur d'instance
10) Création de déclencheurs avec SQL Developer
11) Nouveautés 11g
E.Porcq : Cours BDD- IUT : DUT Informatique M3106C Année 2019-2020
Sources :
SQL Pour Oracle 3è édition de Christian Soutou Les triggers d’Oracle de J. Akoka & I. Wattiau http://sheikyerbouti.developpez.com/pl_sql/?
page=Chap6
Les déclencheurs (Triggers) avec Oracle
1) Introduction
Les déclencheurs (triggers) sont des programmes associés à des objets dont l'exécution est lancée automatiquement (déclenchée) lors d'une mise à jour ceux-ci.
Ils se programment en PL/SQL; on peut aussi utiliser les langages C, C++, Java etc ... pour créer des déclencheurs.
Ils permettent de :
Programmer des règles de gestion qui n'ont pu être mises en places par des contraintes statiques
Déporter des contraintes au niveau serveur pour alléger le client
Programmer l'intégrité référentielle et la réplication dans les architectures distribuées avec l'utilisation de liens de données (database links).
Les déclencheurs
existent depuis la version 6 d'Oracle.
sont compilables depuis la version 7.3
Permettent la mise à jour de vues multitables depuis la version 8 (instead of)
3
Les déclencheurs (Triggers) avec Oracle
2) Événements déclenchant
Les événements déclencheurs peuvent être
Une instruction insert, update ou delete sur une table ou une vue. On parle de déclencheurs LMD
Une instruction create, alter ou drop sur un objet (table, index, séquence, etc...) On parle de déclencheurs LDD
Le démarrage ou l'arrêt de la base (startup ou shutdown), une erreur spécifique (no_data_found, dup_val_on_index, etc...), une connexion ou une déconnexion d'un utilisateur. On parle de déclencheurs d'instances 8 (instead of)
Les déclencheurs (Triggers) avec Oracle
3) Mécanisme général
Une fois codé puis compilé, le déclencheur est stocké dans la base En cas d'événement approprié, si le déclencheur est actif, il s'exécute
Le bloc PL/SQL qui constitue le trigger peut être exécuté avant ou après la vérification des contraintes d'intégrité
Il peut être exécuté pour chaque ligne affectée par l'ordre LMD ou bien une seule fois pour la commande
Un déclencheur s'exécute dans le cadre d'une transaction. Il ne peut donc pas contenir d'instruction commit ou rollback ou toute instruction générant une fin de transaction implicite (ordre LDD)
5
Les déclencheurs (Triggers) avec Oracle
4) Privilèges systèmes
Pour créer des déclencheurs, il faut disposer des privilèges systèmes CREATE TRIGGER (présent avec le rôle RESOURCE)
CREATE ANY TRIGGER (pour ceux des autres schémas)
ADMINISTER DATABSE TRIGGER (pour les déclencheurs d'instances)
Les déclencheurs (Triggers) avec Oracle
5) Création/destruction/manipulation d’un déclencheur
La syntaxe de création du déclencheur commence par :
CREATE [OR REPLACE] TRIGGER [<schéma>].<nom du déclencheur>
La syntaxe de destruction du déclencheur est :
DROP TRIGGER [<schéma>].<nom du déclencheur>
La syntaxe pour renommer un déclencheur est :
ALTER TRIGGER [<schéma>].<nom du déclencheur> RENAME TO [<schéma>].<nouveau nom du déclencheur>
La syntaxe pour compiler un déclencheur est :
ALTER TRIGGER [<schéma>].<nom du déclencheur> COMPILE;
La syntaxe pour rendre un déclencheur actif ou inactif :
ALTER TRIGGER [<schéma>].<nom du déclencheur> {ENABLE|
DISABLE};
Le nom doit être unique dans un même schéma.
Il peut avoir le nom d'un autre objet (table, vue, procédure) mais à éviter.
Ex : CREATE TRIGGER trigInsertCoureur
7
Les déclencheurs (Triggers) avec Oracle
6) Option before ou after
Elle précise le moment de l'exécution du déclencheur (avant ou après l'opération).
Avec l’option before, le déclencheur s’exécute avant une éventuelle contrainte d’intégrité portant sur la même table et/ou colonne.
Avec l’option after, le déclencheur s’exécutera peut être après une éventuelle contrainte d’intégrité portant sur la même table et/ou colonne.
Si le déclencheur n'a pas pour but d'interdire éventuellement l'opération déclenchante, il est préférable qu'il soit de type after
Dans un trigger after, la table concernée sera affectée avant l’exécution du trigger.
Ex : CREATE TRIGGER trigInsertCoureur before insert ...
Les déclencheurs (Triggers) avec Oracle
6) Option before ou after
Exemple 1
drop table essai;
create table essai ( a int,
b int,
constraint pk1 check (a is not null) );
CREATE OR REPLACE TRIGGER DEC_ESSAI XXXX
INSERT ON ESSAI for each row BEGIN
if :new.a is null then
raise_application_error(-20001,'pas nul');
else
dbms_output.put_line('ok');
end if;
dbms_output.put_line('fin');
END;
/
insert into essai values (null,4);
Avec cet exemple, on voit que before place l’exécution du déclencheur prioritaire sur la contrainte statique. After le place après la contrainte.
Avec before
Erreur SQL : ORA-20001: pas nul ORA-06512: à "ERIC.DEC_ESSAI", ligne 3
ORA-04088: erreur lors d'exécution du déclencheur 'ERIC.DEC_ESSAI'
Avec after
Erreur SQL : ORA-02290: violation de contraintes (ERIC.PK1) de
vérification
02290. 00000 - "check constraint (%s.%s) violated"
9
Les déclencheurs (Triggers) avec Oracle
6) Option before ou after
Exemple 2
drop table essai;
create table essai (a int, b char(10));
create or replace trigger trig_compte_essai XXX insert on essai
declare nb int;
begin
select count(*) into nb from essai;
dbms_output.put_line('nb :' || nb);
end;
/
insert into essai values (1,'toto');
Avec cet exemple, on voit que qu’avec before, le déclencheur s’exécute avant
l’insertion de données. C’est le contraire avec after.
Avec before
Avec after
Les déclencheurs (Triggers) avec Oracle
7) Déclencheur LMD 7-1) Déclencheur global ou ligne
Ils sont lancés par une opération insert ou update ou delete Le même déclencheur peut s'activer par les trois opérations
Pour update, on peut spécifier une liste de colonnes. Dans ce cas, le trigger ne se déclenchera que si l'instruction update porte sur l'une au moins des colonnes précisée dans la liste.
Ex : CREATE TRIGGER trigInsertCoureur before insert or delete on tdf_coureur
7-1) Déclencheur global ou ligne
un déclencheur ligne (row trigger) est exécuté pour chaque ligne concernée par l'opération LMD. On le distingue par la directive " for each row ".
un déclencheur global ou d'état (statement trigger) ou d'instruction ne s'exécute qu'une fois par instruction LMD
Ex : CREATE TRIGGER trigInsertCoureur before insert or update on tdf_coureur for each row
11
Les déclencheurs (Triggers) avec Oracle
7) Déclencheur LMD 7-2) Déclencheur ligne
avec ce type de déclencheur, on peut avoir accès (suivant l'opération LMD) à l'ancienne et/ou à la nouvelle donnée affectant la table.
pour un insert, toutes les colonnes de la ligne insérée sont accessibles. Elles se nomment :new.<nom de la colonne>
pour un delete, toutes les colonnes de la ligne supprimée sont accessibles.
Elles se nomment :old.<nom de la colonne>
pour un update, toutes les colonnes de la ligne supprimée et insérées sont accessibles. Elles se nomment :new.<nom de la colonne> :old.<nom de la colonne>
il est possible de restreindre l'exécution du déclencheur avec la clause when.
Si l'expression when n'est pas vérifiée le déclencheur ne s'exécute pas.
Ex CREATE TRIGGER trigInsertCoureur before insert or update on tdf_coureur for each row when (new.code_tdf = 'FRA')
La clause referencing peut permettre de renommer new et old
Ex CREATE TRIGGER trigInsertCoureur before insert or update on tdf_coureur for each row referencing NEW as nouveau
Les déclencheurs (Triggers) avec Oracle
7) Déclencheur LMD 7-2) Déclencheur ligne
Lorsque le même déclencheur peut être exécuté à partir d'opérations LMD
différentes, il est possible de tester dans le programme l'événement déclencheur avec les prédicats
If inserting then If deleting then if updating then
If updating[('<colonne>')] then
Exemple de déclencheur ligne
create or replace trigger SONDAGE_TRIG1 after insert or update on SONDAGE
for each row begin
dbms_output.put_line('début SONDAGE_TRIG1');
If inserting then
insert into sondage_copie values (
:new.num,:new.date_naissance,
:new.reponse1, :new.reponse2,:new.val );
End if ;
dbms_output.put_line('fin SONDAGE_TRIG1');
end;
13
Les déclencheurs (Triggers) avec Oracle
7) Déclencheur LMD 7-2) Déclencheur ligne
Lorsqu'un déclencheur s'exécute pour un update sur quelques colonnes, les attributs non utilisés prennent dans le programme les anciennes valeurs des enregistrements concernés : old = new
Exemple de déclencheur ligne
update sondage set val=10 where val=9;
CREATE OR REPLACE TRIGGER SONDAGE_TRIG0 AFTER UPDATE ON SONDAGE
FOR EACH ROW BEGIN
dbms_output.put_line('début SONDAGE_TRIG0');
dbms_output.put_line(:new.num||' '||:new.reponse1||' '||:new.reponse2||' '||:new.val);
dbms_output.put_line(:old.num||' '||:old.reponse1||' '||:old.reponse2||' '||:old.val);
dbms_output.put_line('fin SONDAGE_TRIG0');
END;
début SONDAGE_TRIG0 69 oui non 10 69 oui non 9 fin SONDAGE_TRIG0 début SONDAGE_TRIG0 70 oui non 10 70 oui non 9 fin SONDAGE_TRIG0
Les déclencheurs (Triggers) avec Oracle
7) Déclencheur LMD 7-3) Table en mutation
Un déclencheur ligne ne peut pas modifier la table concernée (appelée aussi table mutante) par l'instruction (insert, update ou delete) qui a déclenché ce trigger
Il peut éventuellement lire la table dans le cas d'un déclenchement par un insert de type before
CREATE OR REPLACE TRIGGER TRIGGER_MUTANT BEFORE UPDATE ON TDF_COUREUR
for each row BEGIN
update tdf_coureur set date_insert = sysdate where n_coureur = :new.n_coureur;
END;
Test : update tdf_coureur set nom = 'TAYOU' where nom = 'HINAULT'
Erreur SQL : ORA-04091: table PATRICE.TDF_COUREUR is mutating, trigger/function may not see it
ORA-06512: at "PATRICE.TRIGGER_MUTANT", line 2
ORA-04088: error during execution of trigger 'PATRICE.TRIGGER_MUTANT'
04091. 00000 - "table %s.%s is mutating, trigger/function may not see it"
*Cause: A trigger (or a user defined plsql function that is referenced in this statement) attempted to look at (or modify) a table that was in the middle of being modified by the statement which fired it.
*Action: Rewrite the trigger (or function) so it does not read that table.
X X
X X
Tables15
Les déclencheurs (Triggers) avec Oracle
7) Déclencheur LMD 7-4) Déclencheur d’état
Ils n'ont pas accès aux valeurs mises à jours (:new et :old) par l'opération puisqu'ils se déclenchent une seule fois même si la requête LMD met à jour plusieurs lignes.
Ils présentent l'avantage de pouvoir effectuer des manipulations sur la table qui a déclenché le trigger.
Ils sont plus performants en général
create or replace trigger SONDAGE_TRIG_612B AFTER insert on SONDAGE begin
dbms_output.put_line('début SONDAGE_TRIG_612B');
insert into sondage_copie select * from sondage where num not in ( select num from sondage_copie );
dbms_output.put_line('fin SONDAGE_TRIG_612B');
end;
/
Les déclencheurs (Triggers) avec Oracle
7) Déclencheur LMD 7-5) Avantage et inconvénients des déclencheurs ligne et globaux
Un trigger pouvant avorter la requête déclenchante doit toujours être de type ligne (for each row)
Un trigger n’avortant jamais la requête déclenchante peut être de type ligne ou global mais est souvent plus efficace en global
Il y a risque de mutation avec un trigger de type ligne :new et:old n’ont aucun sens avec un trigger global
Ligne global requête sur la table déclenchant le programme Non * Oui
utilisation de:new ou:old Oui Non
* : sauf select dans le cas d’un déclencheur before insert
17
Les déclencheurs (Triggers) avec Oracle
7) Déclencheur LMD 7-6) Déclencheur Instead of
il permet de mettre à jour une vue multitable ce qui n'est pas toujours possible avec une requête LMD standard. En effet la mise à jour n'est possible dans une vue que
si on effectue la mise à jour uniquement dans une table si une table est protégée par clé
Si elle ne comporte pas de distinct, de group by, d’ensemble, de sous- requête
(voir exemple page suivante)
il utilise la clause for each row (implicite) il n'utilise pas la les options before et after
il ne permet pas de préciser le nom d'une colonne pour un update il ne permet pas non plus d'utiliser la clause when
18
Les déclencheurs (Triggers) avec Oracle
7) Déclencheur LMD 7-6) Déclencheur Instead of
création d'une vue modifiable (v_article_fournisseur1)
create view v_article_fournisseur1 as
select fo.fo_nom, ar.fo_numero,ar_numero,ar_nom,ar_poids,ar_couleur, ar_stock,ar_pa,ar_pv from cdi_article ar
join cdi_fournisseur fo on ar.fo_numero = fo.fo_numero;
create view v_article_fournisseur2 as
select fo.fo_nom, fo.fo_numero,ar_numero,ar_nom,ar_poids,ar_couleur, ar_stock,ar_pa,ar_pv from cdi_article ar
join cdi_fournisseur fo on ar.fo_numero = fo.fo_numero;
insert into v_article_fournisseur1 (fo_numero,ar_numero, ar_nom,ar_poids,ar_couleur,ar_stock,ar_pa,ar_pv)
values ('F06','A85','GOMME','25','BLANC',20,1,2);
insert into v_article_fournisseur2 (fo_numero,ar_numero, ar_nom,ar_poids,ar_couleur,ar_stock,ar_pa,ar_pv)
values ('F06','A86','GOMME','25','BLANC',20,1,2);
ORA-01779: impossible de modifier une colonne correspondant à une table non protégée par clé
select * from user_updatable_columns where lower(table_name) like 'v_article_fournisseur%' and column_name like '%NUMERO%';
OWNER TABLE_NAME COLUMN_NAME UPDATABLE INSERTABLE DELETABLE CDI V_ARTICLE_FOURNISSEUR1 FO_NUMERO YES YES YES CDI V_ARTICLE_FOURNISSEUR1 AR_NUMERO YES YES YES CDI V_ARTICLE_FOURNISSEUR2 FO_NUMERO NO NO NO CDI V_ARTICLE_FOURNISSEUR2 AR_NUMERO YES YES YES
19
Les déclencheurs (Triggers) avec Oracle
7) Déclencheur LMD 7-6) Déclencheur Instead of
Ce trigger permet de modifier aussi bien article que fournisseur, ce qui est impossible normalement.
create or replace trigger art_four_trig1 instead of insert on v_article_fournisseur1 Declare
vNbFour int := 0; vNbArt int := 0;
Begin
select count(*) into vNbFour from cdi_fournisseur where fo_numero=:new.fo_numero;
select count(*) into vNbArt from cdi_article where ar_numero = :new.ar_numero;
if vnbart<>0 and vnbfour<>0 then
raise_application_error (-20001,'l''article et le fournisseur existent déjà');
elsif vnbart=0 then
insert into cdi_article (fo_numero,ar_numero,ar_nom,ar_poids,ar_couleur, ar_stock,ar_pa,ar_pv) values (:new.fo_numero,:new.ar_numero,:new.ar_nom, :new.ar_poids,:new.ar_couleur,:new.ar_stock,:new.ar_pa,:new.ar_pv);
Else
insert into cdi_fournisseur (fo_numero,fo_nom) values (:new.fo_numero,null);
end if;
dbms_output.put_line('v_article_fournisseur1 terminé');
end;
Les déclencheurs (Triggers) avec Oracle
8) Déclencheur LDD
Ils réagissent aux modifications de la structure de la base de données Ils sont sensibles aux options before et after
la directive database précise que le déclencheur peut s'exécuter à partir d'un événement provoqué par n'importe quel schéma
la directive schema précise que le déclencheur peut s'exécuter à partir d'un événement provoqué par le schéma lui même
Les ordres LDD pouvant provoquer l'exécution du déclencheur sont : alter, comment, create, drop, grant, rename, revoke
Ex :
create trigger majTDF before drop on iut123.schema Begin
if to_char(sysdate,'DAY') = 'DIMANCHE' then raise_application_error
(-20001,'pas de destruction le dimanche');
end if;
end;
21
Les déclencheurs (Triggers) avec Oracle
9) Déclencheur d'instance
Des événements systèmes peuvent provoquer le déclenchement d'un code PL/SQL
Des événements comme logon, startup, serverrror, suspend utilisent l'option after
Des événements comme LOGOFF, SHUTDOWN utilisent l'option before
Des événements comme AFTER STARUP et BEFORE SHUTDOWN s'appliquent avec des déclencheurs de type DATABASE
Ex :
create or replace
trigger deconnexion before LOGOFF on DATABASE begin
insert into trace values (user,sysdate);
end;
Les déclencheurs (Triggers) avec Oracle
10) Création de déclencheurs avec SQL Developer
Il est possible d'utiliser cette interface pour créer les déclencheurs
Utilisateur propriétaire Nom du déclencheur
Table Vue Schema Database Schéma concerné
Objet concerné Before after
Renommage old et new
Ligne ou global Événement déclenchant
23
Les déclencheurs (Triggers) avec Oracle
11) Nouveautés 11g
Il est possible de créer des déclencheurs directement inactifs (disable) Il est possible de choisir l'ordre d'exécution des déclencheurs d'un même événement (utilisation de la directive follows)
Il est possible de créer des déclencheurs composés de plusieurs blocs sensibles à des événements différents.
Cela peut permettre de résoudre le problème des tables mutantes.
Cela peut aussi permettre de réduire le nombre de déclencheurs