只有在行更改后,MySQL才会触发更新

只有在数据被真正改变的情况下,才有可能使用“更新后”触发器。 我知道“新和旧”。 但是,使用它们时,我只能比较列。 例如“NEW.count <> OLD.count”。

但我想要的东西:运行触发如果“新<>老”

一个例子:

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 | +------+------+ 

重点是,有一个更新,但没有任何改变 。 但触发器仍然运行。 恕我直言,应该有一种方式,不。

我知道我可以使用

如果NOW.b <> OLD.b

对于这个例子。

但想象一个大的桌子,改变列。 你必须比较每一列,如果数据库发生变化,你必须调整触发器。 而且它并不“感觉”比较硬编码的行的每一列:)

加成

正如你所看到的那样

行匹配:1更改:0警告:0

MySQL知道该行没有改变。 但它并不与触发器分享这个知识。 像“后更新”或类似这样的触发器将是很酷的。

作为一种解决方法,您可以使用时间戳(旧的和新的)进行检查,但是在没有对该行进行任何更改时不会更新该时间戳。 (可能这是造成混淆的原因?因为那个也被称为“on update”,但是当没有改变发生时不会被执行)在一秒钟之内的改变将不执行那部分触发器,但是在某些情况下可能是好的(就像你有一个拒绝快速改变的应用程序一样)。

例如,而不是

 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 

你可以使用

 IF NEW.ts <> OLD.ts THEN INSERT INTO bar (a, b) VALUES(NEW.a, NEW.b) ; END IF 

那么你每次更新计划时都不必改变触发器(问题中提到的问题)。

编辑:添加完整的例子

 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) 

这是因为mysql的处理时间戳的行为。 时间戳只有在更新发生更改时才会更新。

文档在这里:
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 | +-------+-----------+------+-----+-------------------+-----------------------------+ 

但想象一个大的桌子,改变列。 你必须比较每一列,如果数据库发生变化,你必须调整触发器。 而且它并不“感觉”很好比较硬编码的每一行:)

是的,但这是继续的方式。

作为一个方面说明,在更新之前抢先检查也是一个很好的做法:

 UPDATE foo SET b = 3 WHERE a=3 and b <> 3; 

在你的例子中,这将使它更新(从而覆盖行而不是三行。

我不能评论,所以要小心,如果你的列支持NULL值,OLD.x <> NEW.x是不够的,因为

SELECT IF(1<>NULL,1,0)

返回0与之相同

 NULL<>NULL 1<>NULL 0<>NULL 'AAA'<>NULL 

所以它不会跟踪更改FROM和TO NULL

在这种情况下正确的方法是

 ((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)) 

您可以通过使用NULL安全的等号运算符<=>比较每个字段,然后使用NOT否定结果 。

完整的触发器将变成:

 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 ; 

(根据我的不同答案 )

在这里,如果有任何行影响新的插入然后它会更新不同的表在数据库中。

 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 ; 
 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 

使用以下查询来查看哪些行有更改:

 (select * from inserted) except (select * from deleted) 

这个查询的结果应该包含所有与以前不同的新logging。