Existe-t-il une possibilité d'utiliser un déclencheur "après mise à jour" uniquement dans le cas où les données ont été VRAIMENT modifiées. Je connais "NEW and OLD". Mais lorsque je les utilise, je ne peux comparer que des colonnes. Par exemple "NEW.count <> OLD.count".
Mais je veux quelque chose comme: run trigger if "NEW <> OLD"
Un exemple:
create table foo (a INT, b INT);
create table bar (a INT, b INT);
INSERT INTO foo VALUES(1,1);
INSERT INTO foo VALUES(2,2);
INSERT INTO foo VALUES(3,3);
CREATE TRIGGER ins_sum
AFTER UPDATE ON foo
FOR EACH ROW
INSERT INTO bar VALUES(NEW.a, NEW.b);
UPDATE foo SET b = 3 WHERE a=3;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0
select * from bar;
+------+------+
| a | b |
+------+------+
| 3 | 3 |
+------+------+
Le fait est qu'il y a eu une mise à jour, mais rien n'a changé. Mais la gâchette fonctionnait quand même. IMHO il devrait y avoir un moyen que ce ne soit pas.
Je sais que j'aurais pu utiliser
IF NOW.b <> OLD.b
pour cet exemple.
MAIS imaginez une grande table avec des colonnes changeantes. Vous devez comparer chaque colonne et si la base de données change, vous devez ajuster le déclencheur. ET il n'est pas "agréable" de comparer chaque colonne de la ligne codée en dur :)
Ajout
Comme vous pouvez le voir sur la ligne
Lignes correspondantes: 1 Changement: 0 Avertissements: 0
MySQL sait que la ligne n'a pas changé. Mais il ne partage pas cette connaissance avec la gâchette. Un déclencheur du type "AFTER REAL UPDATE" ou quelque chose comme cela serait cool.
En guise de solution de contournement, vous pouvez utiliser l'horodatage (ancien et nouveau) pour vérifier si celui-ci est pas mis à jour lorsqu'il n'y a aucune modification dans la ligne. (Peut-être que c'est la source de confusion? Parce que celui-ci est aussi appelé 'on update' mais n'est pas exécuté si aucun changement ne survient) Les changements en une seconde n'exécuteront alors pas cette partie du déclencheur, mais dans certains cas cela pourrait être correct (comme quand vous avez une application qui rejette quand même les changements rapides.)
Par exemple, plutôt que
IF NEW.a <> OLD.a or NEW.b <> OLD.b /* etc, all the way to NEW.z <> OLD.z */
THEN
INSERT INTO bar (a, b) VALUES(NEW.a, NEW.b) ;
END IF
vous pourriez utiliser
IF NEW.ts <> OLD.ts
THEN
INSERT INTO bar (a, b) VALUES(NEW.a, NEW.b) ;
END IF
Ensuite, vous ne devez pas modifier votre déclencheur à chaque fois que vous mettez à jour le schéma (le problème que vous avez mentionné dans la question.)
EDIT: Exemple complet ajouté
create table foo (a INT, b INT, ts TIMESTAMP);
create table bar (a INT, b INT);
INSERT INTO foo (a,b) VALUES(1,1);
INSERT INTO foo (a,b) VALUES(2,2);
INSERT INTO foo (a,b) VALUES(3,3);
DELIMITER ///
CREATE TRIGGER ins_sum AFTER UPDATE ON foo
FOR EACH ROW
BEGIN
IF NEW.ts <> OLD.ts THEN
INSERT INTO bar (a, b) VALUES(NEW.a, NEW.b);
END IF;
END;
///
DELIMITER ;
select * from foo;
+------+------+---------------------+
| a | b | ts |
+------+------+---------------------+
| 1 | 1 | 2011-06-14 09:29:46 |
| 2 | 2 | 2011-06-14 09:29:46 |
| 3 | 3 | 2011-06-14 09:29:46 |
+------+------+---------------------+
3 rows in set (0.00 sec)
-- UPDATE without change
UPDATE foo SET b = 3 WHERE a = 3;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0
-- the timestamo didnt change
select * from foo WHERE a = 3;
+------+------+---------------------+
| a | b | ts |
+------+------+---------------------+
| 3 | 3 | 2011-06-14 09:29:46 |
+------+------+---------------------+
1 rows in set (0.00 sec)
-- the trigger didn't run
select * from bar;
Empty set (0.00 sec)
-- UPDATE with change
UPDATE foo SET b = 4 WHERE a=3;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
-- the timestamp changed
select * from foo;
+------+------+---------------------+
| a | b | ts |
+------+------+---------------------+
| 1 | 1 | 2011-06-14 09:29:46 |
| 2 | 2 | 2011-06-14 09:29:46 |
| 3 | 4 | 2011-06-14 09:34:59 |
+------+------+---------------------+
3 rows in set (0.00 sec)
-- and the trigger ran
select * from bar;
+------+------+---------------------+
| a | b | ts |
+------+------+---------------------+
| 3 | 4 | 2011-06-14 09:34:59 |
+------+------+---------------------+
1 row in set (0.00 sec)
Cela fonctionne à cause du comportement de mysql sur la gestion des horodatages. L'horodatage n'est mis à jour que si une modification est survenue dans les mises à jour.
La documentation est ici:
https://dev.mysql.com/doc/refman/5.7/en/timestamp-initialization.html
desc foo;
+-------+-----------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------+------+-----+-------------------+-----------------------------+
| a | int(11) | YES | | NULL | |
| b | int(11) | YES | | NULL | |
| ts | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-------+-----------+------+-----+-------------------+-----------------------------+
MAIS imaginez une grande table avec des colonnes changeantes. Vous devez comparer chaque colonne et si la base de données change, vous devez ajuster le déclencheur. ET il n'est pas "agréable" de comparer chaque ligne codée en dur :)
Oui, mais c'est la façon de procéder.
En passant, il est également judicieux de vérifier avant la mise à jour:
UPDATE foo SET b = 3 WHERE a=3 and b <> 3;
Dans votre exemple, cela le ferait mettre à jour (et donc écraser) deux lignes au lieu de trois.
Attention, si votre colonne prend en charge les valeurs NULL, OLD.x <> NEW.x ne suffit pas, car
SELECT IF(1<>NULL,1,0)
renvoie 0 comme identique à
NULL<>NULL 1<>NULL 0<>NULL 'AAA'<>NULL
Donc, il ne suivra pas les changements FROM et TO NULL
La manière correcte dans ce scénario est
((OLD.x IS NULL AND NEW.x IS NOT NULL) OR (OLD.x IS NOT NULL AND NEW.x IS NULL) OR (OLD.x<>NEW.x))
Vous pouvez le faire en comparant chaque champ à l'aide de l'opérateur opérateur égal à NULL-safe <=>
et ensuite nier le résultat avec NOT
.
Le déclencheur complet deviendrait:
DROP TRIGGER IF EXISTS `my_trigger_name`;
DELIMITER $$
CREATE TRIGGER `my_trigger_name` AFTER UPDATE ON `my_table_name` FOR EACH ROW
BEGIN
/*Add any fields you want to compare here*/
IF !(OLD.a <=> NEW.a AND OLD.b <=> NEW.b) THEN
INSERT INTO `my_other_table` (
`a`,
`b`
) VALUES (
NEW.`a`,
NEW.`b`
);
END IF;
END;$$
DELIMITER ;
(Basé sur un réponse différente de la mienne .)
Ici, s'il y a une ligne affectant la nouvelle insertion, elle sera mise à jour sur une autre table de la base de données.
DELIMITER $$
CREATE TRIGGER "give trigger name" AFTER INSERT ON "table name"
FOR EACH ROW
BEGIN
INSERT INTO "give table name you want to add the new insertion on previously given table" (id,name,age) VALUES (10,"sumith",24);
END;
$$
DELIMITER ;
Utilisez la requête suivante pour voir quelles lignes ont été modifiées:
(select * from inserted) except (select * from deleted)
Les résultats de cette requête doivent contenir tous les nouveaux enregistrements différents des anciens.
MYSQL TRIGGER BEFORE UPDATE IF OLD.a<>NEW.b
USE `pdvsa_ent_aycg`;
DELIMITER $$
CREATE TRIGGER `cisterna_BUPD` BEFORE UPDATE ON `cisterna` FOR EACH ROW
BEGIN
IF OLD.id_cisterna_estado<>NEW.id_cisterna_estado OR OLD.observacion_cisterna_estado<>NEW.observacion_cisterna_estado OR OLD.fecha_cisterna_estado<>NEW.fecha_cisterna_estado
THEN
INSERT INTO cisterna_estado_modificaciones(nro_cisterna_estado, id_cisterna_estado, observacion_cisterna_estado, fecha_cisterna_estado) values (NULL, OLD.id_cisterna_estado, OLD.observacion_cisterna_estado, OLD.fecha_cisterna_estado);
END IF;
END