Déclencheurs (Triggers)
Les triggers sont des instructions SQL stockées qui sont automatiquement exécutées lorsqu’un événement spécifique se produit dans une base de données.
On peut les assimmiler à des observateurs qui réagissent à des événements spécifiques :
- juste avant ou juste après une insertion, une mise à jour ou une suppression de données.
Les déclencheurs permettent de définir des contraintes dynamiques.
Utilisation des déclencheurs
Les déclencheurs sont utilisés pour :
- Vérifier l’intégrité des données
- Mettre à jour des données dans une table en fonction des modifications apportées à une autre table
- Auditer les modifications apportées aux données
- Réagir à des événements spécifiques
Syntaxe
1CREATE TRIGGER nom_declencheur2{ BEFORE | AFTER | INSTEAD } OF { INSERT | UPDATE | DELETE }3ON nom_table4{ FOR EACH ROW | FOR EACH STATEMENT }5BEGIN6 -- Instructions7END//
- BEFORE | AFTER | INSTEAD OF : détermine le moment où le déclencheur est exécuté
- INSERT | UPDATE | DELETE : détermine l’événement qui déclenche le triggerf
- FOR EACH ROW | FOR EACH STATEMENT : détermine si le trigger est exécuté pour chaque ligne ou pour chaque instruction
DELIMITER
Remarque
Avec SQLite, il n’y a pas besoin de changer le délimiteur pour les triggers oun les procédures. SQLite détecte automatiquement le début et la fin des instructions.
Avant de présenter un exemple de trigger, il est important de noter que MySQL utilise le point-virgule (;
) pour terminer les instructions SQL.
Le point-virgule permet donc de distinguer, ou de délimiter les instructions SQL.
On dit donc que le point-virgule est le délimiteur par défaut des instructions SQL.
Se pose alors le problème du bloc d’instructions des triggers qui contiennent aussi des points-virgules. Comment savoir si un point-virgule termine une instruction SQL ou un bloc d’instructions du Trigger ?
Pour adresser ce problème, on peut utiliser le mot-clé DELIMITER
pour changer le délimiteur temporairement.
1DELIMITER // -- On change le délimiteur par défaut ";" par "//"2-- On peut utiliser n'importe quel caractère qui n'est pas utilisé dans le code SQL3CREATE TRIGGER nom_declencheur4AFTER INSERT ON nom_table5BEGIN6 SELECT * FROM nom_table; -- ici le point-virgule ne termine pas l'instruction,7 -- SQL comprend que le bloc d'instructions et donc le Trigger n'est pas terminé8END// -- On termine le code du trigger avec le nouveau délimiteur9DELIMITER ; -- On rétablit le délimiteur par défaut
Exemple
Soit le schéma de base de données suivant :
1CREATE TABLE Comptes (2 id INT PRIMARY KEY,3 solde INT NOT NULL DEFAULT 0 CHECK (solde >= 0)4);5
6CREATE TABLE Commandes (7 id INT PRIMARY KEY,8 id_compte INT,9 montant INT NOT NULL,10 FOREIGN KEY (id_compte) REFERENCES Comptes(id)11);
On souhaite que le solde des comptes soit automatiquement mis à jour après chaque nouvelle commande en décrémentant le solde par le montant de la commande.
On va donc créer le trigger suivant :
1DELIMITER //2CREATE TRIGGER maj_solde3AFTER INSERT ON Commandes4BEGIN5 UPDATE Comptes6 SET solde = solde - NEW.montant7 WHERE id = NEW.id_compte;8END//9DELIMITER ;
Version commentée :
1DELIMITER // -- On change le délimiteur par défaut ";" par //2CREATE TRIGGER maj_solde -- On créer un déclencheur nommé "maj_solde"3AFTER INSERT ON Commandes -- Qui sera déclenché automatiquement après chaque insertion dans la table Commandes4FOR EACH ROW -- Pour chaque nouvelle ligne insérée, execute le bloc d'instructions suivant5BEGIN -- Début du bloc d'instructions6 UPDATE Comptes -- Met à jour la table Comptes7 SET solde = solde - NEW.montant -- Décrémente le solde du compte du montant de la commande8 WHERE id = NEW.id_compte; -- Pour le compte associé à la commande9END//10DELIMITER ; -- On rétablit le délimiteur par défaut
Instructions NEW et OLD
Dans un déclencheur, on peut utiliser les mots-clés NEW
et OLD
pour accéder aux valeurs des colonnes de la ligne insérée, mise à jour supprimée.
NEW
: contient les nouvelles valeurs de la ligneOLD
: contient les anciennes valeurs de la ligne
Exemple avec NEW et une insertion
Reprenons l’exemple du trigger précédent :
1DELIMITER //2CREATE TRIGGER maj_solde3AFTER INSERT ON Commandes4FOR EACH ROW5BEGIN6 UPDATE Comptes7 SET solde = solde - NEW.montant8 WHERE id = NEW.id_compte;9END//10DELIMITER ;
Si on ajoute maintenant une nouvelle commande :
1INSERT INTO Commandes (id, id_compte, montant) VALUES (1, 1, 100);
À l’execution de cette insertion :
- le trigger va se déclencher automatiquement
- mettre à jour le solde du compte 1 en décrémentant de 100.
Dans le code d’instructions du trigger :
NEW.montant
fait référence à la valeur de la colonnemontant
de la nouvelle ligne insérée, soit ici la valeur 100.NEW.id_compte
fait référence à la valeur de la colonneid_compte
de la nouvelle ligne insérée, soit ici la valeur 1.
Exemple avec OLD et une mise à jour
On peut également utiliser le mot-clé OLD
pour accéder aux anciennes valeurs des colonnes lors d’une mise à jour ou d’une suppression.
On souhaite par exemple auditer les modifications apportées aux commandes, c.à.d enregistrer les anciennes et nouvelles valeurs du montant de la commande.
1DELIMITER //2CREATE TRIGGER audit_commandes3AFTER UPDATE ON Commandes4FOR EACH ROW5BEGIN6 INSERT INTO Commandes_Audit (id_commande, montant_avant, montant_apres)7 VALUES (OLD.id, OLD.montant, NEW.montant);8END//9DELIMITER ;
Dans cet exemple, le trigger audit_commandes
va insérer une nouvelle ligne dans la table Commandes_Audit
à chaque mise à jour d’une commande, contenant l’identifiant de la commande, le montant avant la mise à jour et le montant après la mise à jour.
Exemple d’utilisation :
1UPDATE Commandes SET montant = 200 WHERE id = 1;
À l’execution de cette mise à jour :
- le trigger va se déclencher automatiquement
- insérer une nouvelle ligne dans la table
Commandes_Audit
avec les valeurs suivantes :id_commande
: 1montant_avant
: 100montant_apres
: 200
Dans le code d’instructions du trigger :
OLD.id
fait référence à l’ancienne valeur de la colonneid
de la ligne mise à jour, soit ici la valeur 1.OLD.montant
fait référence à l’ancienne valeur de la colonnemontant
de la ligne mise à jour, soit ici la valeur 100.NEW.montant
fait référence à la nouvelle valeur de la colonnemontant
de la ligne mise à jour, soit ici la valeur 200.
Exemple avec OLD et une suppression
On peut également utiliser le mot-clé OLD
pour accéder aux anciennes valeurs des colonnes lors d’une suppression.
On souhaite par exemple auditer les suppressions de commandes, c.à.d enregistrer les valeurs des commandes supprimées.
1DELIMITER //2CREATE TRIGGER audit_commandes3AFTER DELETE ON Commandes4FOR EACH ROW5BEGIN6 INSERT INTO Commandes_Audit (id_commande, montant_avant, montant_apres)7 VALUES (OLD.id, OLD.montant, 0);8END//9DELIMITER ;
Dans cet exemple, le trigger audit_commandes
va insérer une nouvelle ligne dans la table Commandes_Audit
à chaque suppression d’une commande, contenant l’identifiant de la commande, le montant avant la suppression et 0 comme montant après la suppression.
Exemple d’utilisation :
1DELETE FROM Commandes WHERE id = 1;
À l’execution de cette suppression :
- le trigger va se déclencher automatiquement
- insérer une nouvelle ligne dans la table
Commandes_Audit
avec les valeurs suivantes :id_commande
: 1montant_avant
: 100montant_apres
: 0
Dans le code d’instructions du trigger :
OLD.id
fait référence à l’ancienne valeur de la colonneid
de la ligne supprimée, soit ici la valeur 1.OLD.montant
fait référence à l’ancienne valeur de la colonnemontant
de la ligne supprimée, soit ici la valeur 100.
Suppression d’un déclencheur
Pour supprimer un déclencheur, on utilise la commande DROP TRIGGER
:
1DROP TRIGGER nom_declencheur;
Conclusion
Les déclencheurs sont des procédures stockées qui sont automatiquement exécutées lorsqu’un événement spécifique se produit dans une base de données.
Ils sont utilisés pour vérifier l’intégrité des données, mettre à jour des données en fonction des modifications apportées à une autre table, auditer les modifications apportées aux données et réagir à des événements spécifiques.
Exercice
Soit le schéma de base de données suivant :
1CREATE TABLE Comptes (2 id INT PRIMARY KEY,3 solde INT NOT NULL DEFAULT 0 CHECK (solde >= 0)4);5
6CREATE TABLE Commandes (7 id INT PRIMARY KEY,8 id_compte INT,9 montant INT NOT NULL,10 FOREIGN KEY (id_compte) REFERENCES Comptes(id)11);
Créez un trigger nommé annule_commande
, qui se déclenche à la suppression d’une commande et qui met à jour le solde du compte associé en remboursant le montant de la commande.
Solution
1DELIMITER //2CREATE TRIGGER annule_commande3AFTER DELETE ON Commandes4FOR EACH ROW5BEGIN6 UPDATE Comptes7 SET solde = solde + OLD.montant8 WHERE id = OLD.id_compte;9END//10DELIMITER ;
On ajoute une nouvelle table Details
pour enregistrer les détails des commandes :
1CREATE TABLE Details (2 id INT PRIMARY KEY,3 id_commande INT,4 montant INT NOT NULL,5 description TEXT,6 FOREIGN KEY (id_commande) REFERENCES Commandes(id)7);
Créez un trigger nommé maj_montant_commande
, qui se déclenche à l’insertion d’un détail de commande et qui met à jour le montant de la commande en fonction des montants de tous les détails de la commande.
Aide : utilisez une requête UPDATE
avec une sous-requête SELECT SUM()
.
Solution
1DELIMITER //2CREATE TRIGGER maj_montant_commande3AFTER INSERT ON Details4FOR EACH ROW5BEGIN6 UPDATE Commandes7 SET montant = (SELECT SUM(montant) FROM Details WHERE id_commande = NEW.id_commande)8 WHERE id = NEW.id_commande;9END//10DELIMITER ;