Trois problèmes, trois moments d’action
Une contrainte demande : cette ligne respecte-t-elle toujours une règle ? Une transaction demande : ces opérations doivent-elles réussir ou échouer ensemble ? Un trigger demande : faut-il réagir automatiquement lorsqu’un événement de données survient ?
Le bon choix commence par cette différence. Utiliser un trigger pour tout cache des règles ; utiliser seulement l’application laisse chaque processus réimplémenter les contrôles.
Contrainte : empêcher un état invalide
NOT NULL exige une valeur, UNIQUE évite une répétition, CHECK vérifie une condition et FOREIGN KEY garantit l’existence d’une relation. Ces règles sont déclaratives et visibles dans la structure.
Pour empêcher un prix négatif, CHECK est préférable à un trigger : la règle est courte, atomique et comprise directement par le SGBD.
CREATE TABLE produits (
id_produit INTEGER PRIMARY KEY,
prix NUMERIC NOT NULL CHECK (prix >= 0)
);Transaction : protéger une opération composée
Une commande et son paiement concernent deux tables, mais forment une seule intention métier. BEGIN ouvre la transaction, COMMIT confirme et ROLLBACK annule. SAVEPOINT offre un retour partiel dans une transaction plus longue.
Une transaction ne remplace pas les contraintes : elle garantit l’atomicité des opérations, tandis que les contraintes vérifient chaque état proposé.
BEGIN;
INSERT INTO commandes (id_commande, montant) VALUES (108, 120);
INSERT INTO paiements (id_commande, montant) VALUES (108, 120);
-- COMMIT si tout réussit, sinon ROLLBACK
COMMIT;Trigger : réagir à un événement
PostgreSQL sépare la fonction de trigger et le trigger attaché à la table. OLD contient la ligne avant modification, NEW la ligne proposée. AFTER UPDATE convient à un historique du changement déjà accepté.
BEFORE peut modifier ou refuser NEW, mais une contrainte reste plus claire pour une règle simple. ROW exécute la fonction pour chaque ligne ; STATEMENT l’exécute une fois pour l’instruction.
CREATE FUNCTION journaliser_prix() RETURNS trigger AS $$
BEGIN
INSERT INTO historique_prix (id_produit, ancien_prix, nouveau_prix)
VALUES (OLD.id_produit, OLD.prix, NEW.prix);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER audit_prix
AFTER UPDATE OF prix ON produits
FOR EACH ROW EXECUTE FUNCTION journaliser_prix();Quand ne pas utiliser un trigger
Un trigger convient particulièrement à un audit ou une historisation qui doit s’appliquer quel que soit l’appelant. Même dans ce cas, documentez l’effet, testez les erreurs et mesurez le coût.
- Une contrainte exprime directement la règle simple.
- La logique doit être visible dans un service applicatif et versionnée avec lui.
- Une modification massive déclencherait un traitement coûteux par ligne.
- Le trigger risque de modifier la même table et de créer une récursion.
- L’équipe ne dispose pas des tests et de la supervision nécessaires.
Guide de choix rapide
Demandez d’abord si vous protégez une ligne, un ensemble d’opérations ou une réaction à un événement. Combinez les outils lorsque leurs responsabilités diffèrent : CHECK refuse un prix invalide, une transaction regroupe la modification et son paiement, un trigger conserve l’ancien prix accepté.
Ne choisissez pas sur la longueur du code. Choisissez sur la règle métier, la visibilité, le moment d’exécution, le comportement en erreur et le coût sous charge.
À vous de décider
Associez ces cas : e-mail unique, virement débit/crédit, historique d’une adresse, commande liée à un client et création commande/paiement. Les réponses attendues sont respectivement contrainte, transaction, trigger éventuel, clé étrangère et transaction.
Les micro-formations avancées restent en préparation tant qu’un laboratoire PostgreSQL modifiable et réinitialisable n’a pas passé ses tests de sécurité et de robustesse.
Suivez la préparation de cette compétence.
Consultez le résultat visé, les prérequis et le statut réel de la micro-formation.
