J'ai une table PRODUCTS
avec des colonnes id, name, sku, packaging_type, width, height, weight, case_count
.
Je veux qu'un déclencheur se déclenche quand packaging_type
Est mis à jour. packaging_type
sera une valeur entière.
Il y a une deuxième table PACK_TYPES
avec des colonnes id, width, height, weight, case_count
. C'est là que j'ai besoin d'obtenir les valeurs.
Alors PRODUCTS.packaging_type
commence à mettre à jour, comment mettre à jour PRODUCTS.width
, PRODUCTS.height
, PRODUCTS.weight
, et PRODUCTS.case_count
avec les valeurs correspondantes où PRODUCTS.packaging_type = PACK_TYPES.id
?
Si vous voulez y avoir raison, ce n'est pas la meilleure approche d'une perspective théorique, car vous copiez des données dans votre base de données que vous devriez vraiment dériver.
CREATE ALGORITHM=MERGE VIEW products_with_packaging_info AS
SELECT p.*,
pt.width as packaging_width,
pt.height as packaging_height,
pt.weight as packaging_weight,
pt.case_count AS packaging_case_count
FROM PRODUCTS p
JOIN PACK_TYPES pt ON pt.id = p.packaging_type;
Terminé. SELECT
Les requêtes contre ce point de vue fonctionnent exactement la même chose que les requêtes contre l'une ou l'autre table individuellement, tant que chaque produit a un type de pack. Les requêtes contre cette vue peuvent toujours tirer parti des index sur les tables de base et il n'y a pas de frais générale impliquée dans la copie des attributs d'une table à une autre, ce qui a toujours le potentiel pour mise à jour des anomalies .
Vous pourriez même être surpris de constater que les colonnes de la vue peuvent réellement être mises à jour comme s'il s'agissait d'une table, les mises à jour se propagent dans les tables de base.
J'offre cette suggestion car une base de données bien conçue devrait être telle qu'il est impossible d'obtenir deux réponses différentes à la même question. Par exemple, si une ligne PACK_TYPES est modifiée car une erreur est trouvée, comment s'approprient ses nouvelles valeurs en arrière dans les produits?
Mais si vous voulez vraiment prendre l'approche de déclenchement, cela ressemble à quelque chose comme ça:
DELIMITER $$
DROP TRIGGER IF EXISTS PRODUCTS_bu $$
CREATE TRIGGER PRODUCTS_bu BEFORE UPDATE ON PRODUCTS FOR EACH ROW
BEGIN
IF NOT (NEW.packaging_type <=> OLD.packaging_type) THEN
BEGIN
DECLARE my_width INT DEFAULT NULL; -- using
DECLARE my_height INT DEFAULT NULL; -- the
DECLARE my_weight INT DEFAULT NULL; -- appropriate
DECLARE my_case_count INT DEFAULT NULL; -- data types here
SELECT width, height, weight, case_count
FROM PACK_TYPES
WHERE id = NEW.packaging_type
INTO my_width, my_height, my_weight, my_case_count;
SET NEW.width = my_width, NEW.height = my_height, NEW.weight = my_weight, NEW.case_count = my_case_count;
END;
END IF;
END $$
DELIMITER ;
Le <=>
"spatialhip" est l'opérateur " Opérateur d'égalité null-sécurité " qui limite "non [éventuellement null] = [éventuellement nul]" "" pour toujours être TRUE
ou FALSE
; Ceci est nécessaire car [éventuellement nul]! = [éventuellement null] ne sera jamais vrai si l'expression est NULL
. C'est le cas car, logiquement, "non (faux)" est "vrai" alors que "non (null)" est "null".
J'aurais pu déclarer les variables au début et évité l'intérieur BEGIN
/END
, mais il semble optimal d'éviter ce travail jusqu'à ce que nous sachions que nous devons réellement exécuter la logique intérieure en premier lieu, Ce qui est évité chaque fois que 'emballage_type' n'a pas réellement changé d'une ligne pour une requête de mise à jour donnée. Dans un bloc, les déclarations doivent précéder d'autres déclarations, de sorte que le retardement des déclarations nécessite l'addition du BEGIN
/END
.
Vous voulez également un déclencheur similaire pour BEFORE INSERT
qui serait identique sauf que vous supprimiez les 4 lignes commençant par IF
... BEGIN
... END
... END IF
À partir du corps de la procédure, utilisez un nouveau nom de déclenchement et changez BEFORE UPDATE
à BEFORE INSERT
.
C'est BEFORE
- pas AFTER
- dans les deux cas, car la gâchette déclenche BEFORE
la ligne nouvellement insérée ou nouvelle mise à jour est écrite dans la base de données.
Quelque chose de similaire à cela peut fonctionner:
DROP TRIGGER IF EXISTS BI_PRODUCTS;
DELIMITER //
CREATE TRIGGER BI_PRODUCTS BEFORE INSERT ON PRODUCTS FOR EACH ROW
BEGIN
SET NEW.width := (SELECT width FROM PACK_TYPES WHERE id = NEW.packaging_type);
END //
DELIMITER ;
Cependant, pourquoi stocker les enregistrements de manière double? Ce n'est pas très normalisé. Sauf si vous avez une très bonne raison, une meilleure façon de le faire serait de convertir packaging_type
dans une FOREIGN KEY
de PACK_TYPES
et effectuer un JOIN
Chaque fois que les colonnes des deux tables sont nécessaires.