Bien que les champs calculés constituent une forme de redondance de données, il est parfois commode d’en utiliser afin d’optimiser les performances de certaines requêtes. En effet, l’usage de champs calculés peut servir aux fins par exemple d’éviter des jointures.
On imagine une base de données de gestion de commandes, afin de faciliter les calculs concernant les ventes réalisées, on souhaite :
Quelle solution possible ?
Trigger1 : Quand se déclenche t'il ? Que fait-il ? Testez-le. Contrôlez son exécution
DROP TRIGGER IF EXISTS calculLCBI; DELIMITER $$ CREATE TRIGGER calculLCBI BEFORE INSERT ON ligneCommande FOR EACH ROW BEGIN -- à compléter SELECT prixHT, tauxTVA INTO @prix, @taux FROM produit WHERE idproduit = NEW.idproduit ; -- à compléter SET NEW.totalHTLigne = @prix * NEW.quantite ; SET NEW.totalTVALigne= NEW.totalHTLigne* @taux/100 ; SET NEW.totalTTCLigne= NEW.totalHTLigne + NEW.totalTVALigne; END$$ DELIMITER ;
A vous de jouer : Ecrire et tester les autres déclencheurs
Les mécanismes d’héritage ou de spécialisation peuvent être modélisés. Pour autant, si aucun traitement spécifique n’est fait nous ne sommes pas capables de nous assurer qu’une telle contrainte soit vérifiée.
Dans notre gestion commerciale, un produit est soit un produit alimentaire - et on veut connaître sa date limite de consommation, soit un produit électroménager - et on veut connaitre sa classe énergétique.
Si aucun traitement spécifique n’est fait :
-- Table des produits CREATE TABLE produit ( idProduit INT AUTO_INCREMENT PRIMARY KEY, nomProduit VARCHAR(255) NOT NULL, prixHt DECIMAL(10, 2), -- Prix hors taxe tauxTva DECIMAL(5, 2), -- Taux de TVA en pourcentage typeProduit ENUM('alimentaire', 'electromenager') NOT NULL, -- Type de produit dateLimiteConsommation DATE, -- Date limite pour produits alimentaires classeEnergetique VARCHAR(5), -- Classe énergétique pour électroménager CHECK ( (typeProduit = 'alimentaire' AND dateLimiteConsommation IS NOT NULL AND classeEnergetique IS NULL) OR (typeProduit = 'electromenager' AND classeEnergetique IS NOT NULL AND dateLimiteConsommation IS NULL) ) -- Vérification pour garantir la cohérence des types );
A vous de jouer
A vous de jouer
Explication des contraintes d'intégrité :
A vous de jouer : testez chacune des 2 solutions
On ajoute à notre gestion commerciale la gestion des livraisons des commandes : un produit ne doit pouvoir être livré que si il a été commandé et sa quantité livrée ne doit pas dépasser la quantité commandée bien sûr. On simplifie le cas en autorisant les livraisons des produits en une seule fois.
Le 1er cas est réalisable avec des contraintes d'intégrité référentielle. Comment ?
Pour le 2nd cas il faudra des triggers.
A vous de jouer :
Le couple (Année scolaire, Classe) constitue une promotion. C’est un agrégat. Une telle promotion dispose de plusieurs professeurs et élèves. En l’espèce, la contrainte d’exclusion figurant ci-dessus traduit une règle de gestion des plus simples : pour une classe et une année scolaire données, l’on ne peut être à la fois professeur et élève.
En absence de traitement spécifique, rien ne nous assure qu’un professeur d’une promotion ne participe pas également à la relation élève et réciproquement.
Lorsque l’on ajoute un professeur, il convient qu’il ne figure pas parmi les élèves. Inversement, lorsque l’on ajoute un élève, il convient qu’il ne soit pas professeur. S’il participe aux deux relations il importe d’interdire l’ajout, à savoir l’insertion, voire la modification également.
1er trigger : aucun professeur ne peut être élève.
DROP TRIGGER IF EXISTS check_exclusion_prof ; DELIMITER $$ CREATE TRIGGER check_exclusion_prof BEFORE INSERT, UPDATE ON Professeur FOR EACH ROW BEGIN – DECLARE QTE INT ; -- si le professeur à ajouter/modifier existe en tant qu’élève, c’est mal ! IF ( SELECT COUNT(*) FROM Eleve E WHERE E.NumPersonne = NEW.NumPersonne ) > 0 THEN SIGNAL sqlstate '45000' SET message_text = 'Un professeur d’une promo ne saurait en être un élève' ; END IF ; END $$ DELIMITER ;
2ème trigger : aucun élève ne peut être professeur.
L’on crée le trigger check_exclusion_eleve dont la principale différence avec le précédent trigger est la requête SQL, laquelle devient :
SELECT COUNT(*) FROM Professeur P WHERE P.NumPersonne = NEW.NumPersonne
A l’insertion ainsi qu’à la modification d’une tâche, on historise son état, à savoir qu’on crée une ligne d’historique.
DROP TRIGGER IF EXISTS historise_tache ; DELIMITER $$ CREATE TRIGGER historise_tache BEFORE INSERT, UPDATE ON Tache FOR EACH ROW BEGIN -- crée une nouvelle ligne d’historique INSERT INTO Historique (NumTache, Num, Creation, NumEtat) VALUES ( NEW.Num, SELECT COUNT(*) + 1 FROM Historique WHERE NumTache = NEW.NUM, NOW(), NEW.NumEtat ) ; END $$ DELIMITER ;