37
Les TRIGGERS Introduction Triggers ordre Triggers ligne Triggers E/C/A Traitements différenciés Limitations Ecriture de triggers vérifiant une contrainte d'intégrité Gestion des triggers Application à 9i, 10g ORACLE

Gestion des Transactions dans Oracle7 - kakou0013.free.frkakou0013.free.fr/Cours/Triggers.pdf · Exercice n° 1: Ecrire un trigger ... d'instructions SQL de type LDD ¾CREATE, DROP,

Embed Size (px)

Citation preview

Les TRIGGERS

Introduction

Triggers ordre

Triggers ligne

Triggers E/C/A

Traitements différenciés

Limitations

Ecriture de triggers vérifiant une contrainte d'intégrité

Gestion des triggers

Application à

9i, 10gORACLE

Schéma d ’une compagnie aérienne

AvionNumavCapaciteTypeEntrepot

PiloteMatriculeNomVilleAgeSalaire

Depart

NumvolDate_depNumavMatricule

PassagerNumabNomab

ReservationNumabNumvolDate_dep

VolNumvolHeure_departHeure_arriveeVille_departVille_arrivee

1) Introduction

Un trigger de type Evénement/Action (EA) est composé d'

un événement qui va le déclencher une action à exécuter au

déclenchement

Les triggers d’Oracle

Un trigger est associé à une table

L'événement qui le déclenche est une opération (insertion, suppression, MAJ) sur cette relation;

L'action à exécuter est spécifiée par un bloc PL/SQL.

Typologie des triggers d’Oracle

TRIGGER

ORDRE LIGNE

BEFORE

AFTER BEFORE AFTER

UPDATEINSERT

DELETE UPDATEINSERT

DELETEUPDATE

INSERT

DELETEUPDATE

INSERT

DELETE

2) Triggers ordre

BEFOREAFTER

CREATE TRIGGER nom_du_triggerBEFORE UPDATE ON nom_de_la_relation

BLOC PL/SQL

UPDATEINSERTDELETEUPDATE OF nom_attribut1, nom_attribut2INSERT OR UPDATE OR DELETE

Le bloc PL/SQL sera exécuté avant chaque instruction"UPDATE nom_de_la_relation"

Gestion des triggers

DROP TRIGGER nom_trigger;

ALTER TRIGGER nom_trigger DISABLE;

ALTER TRIGGER nom_trigger ENABLE;

ALTER TABLE nom_table DISABLE ALL TRIGGERS;

ALTER TABLE nom_table ENABLE ALL TRIGGERS;

Exemple

CREATE TABLE TABLEAU_DE_BORD(date_cumul DATE, nombre_res_vendues INTEGER);INSERT INTO TABLEAU_DE_BORD VALUES (sysdate, 0);

CREATE TRIGGER espionAFTER INSERT ON reservation

DECLAREres INTEGER;

BEGINSELECT MAX (nombre_res_vendues) INTO resFROM TABLEAU_DE_BORD;res := res + 1;INSERT INTO TABLEAU_DE_BORD VALUES (sysdate, res);

END;

Exemple

CREATE TRIGGER grèveBEFORE INSERT OR UPDATE OR DELETE ON reservation

BEGINraise_application_error (-20001, 'Toute réservation est

actuellement impossible');END;

Exercices

Exercice n° 1: Ecrire un trigger qui après chaque suppression d'un pilote annule tous les départs prévus le 25 décembre 2003.

Exercice n° 2: Ecrire un ensemble d'instructions (le programme client) qui permettent de déclencher le trigger ci-dessus.

Correction de l'exercice n° 1

Exercice n° 1: Ecrire un trigger qui après chaque suppression d'un pilote annule tousles départs du 25 décembre 1996.

CREATE OR REPLACE TRIGGER T1AFTER DELETE ON pilote

BEGIN

DELETE FROM departWHERE date_dep = '25-12-2003';

END;

Correction de l'exercice n° 2

Exercice n° 2: Ecrire un ensemble d'instructions (le programme client) qui permettent de déclencher le trigger ci-dessus.

BEGIN

DELETE FROM PiloteWHERE Matricule = 10;

END;

3) Triggers lignes : exemple

CREATE TRIGGER incendieBEFORE INSERT OR UPDATE ON reservationFOR EACH ROW

DECLAREres vol.ville_arrivee%TYPE;

BEGIN

SELECT vol.ville_arrivee INTO resFROM volWHERE vol.numvol = :NEW.numvol;

IF res = 'Bruxelles' THEN raise_application_error (-20001, 'Il est impossible de réserver pour

Bruxelles, l''aéroport étant fermé jusqu''à nouvel ordre');END IF;

END;

Exemple

CREATE TRIGGER Déplacement_zéro/* Le pilote doit habiter dans la ville de départ du vol */BEFORE INSERT OR UPDATE ON DepartFOR EACH ROW

DECLAREville_du_pilote pilote.ville%TYPE;ville_depart_vol vol.ville_depart%TYPE;

BEGINSELECT ville INTO ville_du_piloteFROM piloteWHERE matricule = :NEW.matricule;

SELECT ville_depart INTO ville_depart_volFROM volWHERE numvol = :NEW.numvol;

Exemple (suite)

IF ville_du_pilote != ville_depart_vol THEN raise_application_error (-20789, 'Le pilote ' ||

TO_CHAR (:NEW.matricule)|| 'n''habite pas dans laville de départ du vol' || :NEW.numvol');

END IF;

END;/

> INSERT INTO depart VALUES ('AF 567','31-11-1995',1,23);

ORA-20789: Le pilote 23 n'habite pas dans la ville de départ du vol AF 567.

Exercices

Exercice 3: Ecrire un trigger qui empêche de réserver si le nombre de réservations est déjà égal à la capacité de l'avion.

Exercice 4: Ecrire un bloc client qui déclenche le trigger.4.a - Le bloc ne traite pas les erreurs4.b - Le bloc doit traiter l'erreur éventuelle de surréservation

provoquée par le trigger

Nouveautés dans Oracle 8

A partir d'Oracle 8.0Possibilité de créer des triggers INSTEAD OF sur les vues

A partir d'Oracle 8.1 (Oracle 8i)Possibilité de définir des triggers qui se déclenchent lors de l'exécution d'instructions SQL de type LDD

CREATE, DROP, ALTERPossibilité de définir des triggers qui se déclenchent pour un certain nombre d'événements de la base de données

Démarrage ou arrêt de la baseConnexion, déconnexion d'un utilisateur

Conclusion

Les triggers permettent demaintenir des relations contenant des renseignements sur l'activité de la base,rendre le SGBD "actif", en calculant automatiquement certains attributs, en insérant, modifiant ou supprimant des tuples,contrôler dynamiquement certaines manipulations de la base,contrôler l'intégrité,rafraîchir des données dupliquées.

Compléments

Les triggers ECALes traitements différentiésLimitations des triggersMéthode de mise en place de contrainte d ’application avec les triggers

TRIGGER E/C/A: Définition

Un trigger de type Evénement/Condition/Action (ECA) est composé d'

un événement qui va le déclencher,

une condition à vérifier,

une action à exécuter au déclenchement si la condition est vérifiée.

Définition (suite)

UN TRIGGER E/C/A EST ATTACHE A UNE TABLE

L'événement qui le déclenche est une MISE A JOUR sur cette TABLE;

La condition à vérifier porte sur le TUPLE MIS A JOUR, elle est FACULTATIVE;

L'action à exécuter est spécifiée par un BLOC PL/SQL.

La clause WHEN

CREATE TRIGGER controleBEFORE INSERT ON reservationFOR EACH ROWWHEN (new.numab IN (9,23,567,2993))

BEGIN

IF :NEW.numvol LIKE 'AF%'THEN raise_application_error (-20003, 'Passager

recherché, réservation impossible sur Air France');

END IF;

END;

"WHEN" est associé à "FOR EACH ROW"

"new" n'est pas préfixé de ":" dans la clause WHEN

La clause WHEN (suite)

CREATE TRIGGER controleBEFORE INSERT ON reservationFOR EACH ROW

WHEN (new.numab IN (SELECT numabFROM passagerWHERE nomab IN ('Joe', 'Averell')))

BEGINIF :new.numvol LIKE 'AF%' THEN

raise_application_error (-20003, 'Passagerrecherché, réservation impossiblesur Air France');

END IF;END;

Pas de requête dans la

clause WHEN

Traitements différenciés

CREATE TRIGGER grèveBEFORE INSERT OR UPDATE OR DELETE ON reservation

BEGINIF INSERTING THEN

raise_application_error (-20001,'Toute réservation est actuellementimpossible');

ELSIF UPDATING THENraise_application_error (-20002,

'Il est impossible de modifiervotre réservation');

ELSIF DELETING THENraise_application_error (-20003, 'Inutile

d''annuler, le vol n"a pas lieu'');END IF;

END;

INSERTING, UPDATING et DELETING sont des prédicats utilisables par les instructions conditionnelles de PL/SQL.

Conseils

Un seul triggersur la même TABLE avec les mêmes EVENEMENTS de déclenchementau même moment de déclenchementavec la même granularité (ligne ou ordre)

Exemple : T1 : PILOTE, INSERT, AFTER, FOR EACH ROWT2 : PILOTE, INSERT, AFTER, FOR EACH ROW

Cette limite n’est plus vraie à partirdes dernières versions de Oracle7

Possible à partir d’Oracle8Mais pas conseillé

Limitations (suite)

Pas d'instruction COMMITROLLBACKSAVEPOINT

Limitations (suite)

Pas de SELECT sur une table "MUTANTE"

CREATE TRIGGER T1BEFORE INSERT OR DELETE ON piloteFOR EACH ROW

DECLAREres pilote%ROWTYPE;

BEGINSELECT * INTO res FROM pilote WHERE matricule = 1;

END;

ERROR at line 1:ORA-04091: table SCOTT.PILOTE is mutating,trigger may not see it

> DELETE FROM pilote;

Limitations (suite)

Mais ...

CREATE TRIGGER T1BEFORE INSERT OR DELETE ON piloteFOR EACH ROW

DECLAREres pilote%ROWTYPE;

BEGINSELECT * INTO res FROM pilote WHERE matricule = 1;

END;

> INSERT INTO pilote VALUES (12,'Amandier','Clafoutiville'); OK !

Zone de "SELECT FROM Pilote" interdite(pour CREATE TRIGGER on Pilote)

TRIGGER

ORDRE LIGNE "for each row"

BEFORE AFTER

UPDATEINSERT

DELETEUPDATE

INSERT

DELETEUPDATE

INSERT

DELETE

UPDATEINSERT

DELETE

BEFORE AFTER

Écriture de triggers vérifiant une contrainte d'intégrité

Rappel de définition : Une contrainte d'intégrité est une assertion qui doit toujours être vérifiée par les données de la base.Exemples de contraintes d'intégrité :

C1 : L'âge d'un pilote est supérieur à 18.C2 : Deux pilotes différents ont des matricules différents.C3 : La ville de départ d'un vol est différente de la ville d'arrivée.C4 : La date de réservation n'est pas antérieure à la date du jour.C6 : Un "numab" dans la table "réservation" existe aussi dans la table "passager".C7 : Le nombre de réservations pour un départ ne dépasse pas la capacité de l'avion affecté à ce départ.

Écriture de triggers vérifiant une contrainte d'intégrité (suite)

Spécification de la contrainte d'intégrité : Pas de commande unifiée de spécification de contrainte de type CREATE CONSTRAINT (PILOTE.AGE > 18);Mais un ensemble de moyens de spécification :

(1) Dans le "create table"

(1a) Par des mots clésUNIQUENOT NULLFOREIGN KEYPRIMARY KEY

(1b) Contraintes libres monotuples ("check")

(2) Par des triggers

Check (age > 18)Check (ville_arr != ville_dep)Check ( sysdate user )

Écriture de triggers vérifiant une contrainte d'intégrité (suite)

Problème : Etant donnée une contrainte d'intégrité, sur quelles tables doit-on spécifier des triggers ?

Exemple du problème :C7 : Le nombre de réservations pour un départ ne dépasse pas la capacité de l'avion affecté à ce départ.Le trigger créé lors de l'exercice précèdent (ex. 3) suffit-il à maintenir la cohérence de la base par rapport à la contrainte C7, quelles que soient les "insert", "update" et "delete" effectués par les utilisateurs sur la base "compagnie aérienne" ?Sur quelles autres tables doit-on spécifier des triggers ?

Écriture de triggers vérifiant une contrainte d'intégrité (suite)

Méthode suggérée :

Ecrire la requête qui ramènerait les tuples violant la contrainte d'intégrité,

Spécifier un trigger sur toutes les tables apparaissant dans un "from".

Écriture de triggers vérifiant une contrainte d'intégrité (suite)

Exemple d'application de la méthode :

C1 : L'âge d'un pilote est supérieur à 18.

Requête qui ramènerait les tuples violant la contrainte d'intégrité:

Select * from pilote where age < 18.

Il faut un trigger sur PILOTE

Écriture de triggers vérifiant une contrainte d'intégrité (suite)

Exemple d'application de la méthode :C7 : Le nombre de réservations pour un départ ne dépasse pas la capacité de l'avion affecté à ce départ.Requête qui ramènerait les tuples violant la contrainte d'intégrité:SELECT *FROM AVIONWHERE Avion.Capacite <

(SELECT COUNT (*)FROM RESERVATION, DEPARTWHERE Avion.numav = Depart.Numav

AND Reservation.numvol = Depart.numvolAND Reservation.date_dep = Depart.date_dep)

Il faut :un trigger sur AVION, un trigger sur DEPART et un trigger sur RESERVATION

Écriture de triggers vérifiant une contrainte d'intégrité (suite)

Exemple d'écriture du trigger sur AVION :CREATE TRIGGER T3BEFORE INSERT OR UPDATE ON AVIONFOR EACH ROW

DECLAREremplissage_actuel INTEGER;

BEGINSELECT COUNT (*)INTO remplissage_actuelFROM reservation, départWHERE départ.numav = :new.numavand départ.date_dep = reservation.date_depand départ.numvol = reservation.numvol;

IF :new.numav <= remplissage_actuel THEN raise_application_error

(-20002, 'Avion trop petit; violation de la contrainte C7');

END IF;END;/

Écriture de triggers vérifiant une contrainte d'intégrité (suite)

Exemple d'écriture du trigger sur DEPART :CREATE TRIGGER T2BEFORE INSERT OR UPDATE ON DEPARTFOR EACH ROW

DECLARE

remplissage_actuel INTEGER;capacite_avion INTEGER;

BEGIN

SELECT capacite INTO capacite_avionFROM avionWHERE avion.numav = :new.numav;

...

SELECT COUNT (*)INTO remplissage_actuelFROM reservationWHERE numvol = :NEW.numvolAND date_dep = :NEW.date_dep;

IF capacite_avion <= remplissage_actuel THEN

raise_application_error (-20003,'départ avec surnombre, violation de la contrainte C7');

END IF;END;/