jen*_*ens 63 mysql sql database triggers
是否有可能仅在数据被真正改变的情况下使用"更新后"触发器.我知道"新旧".但是在使用它们时我只能比较列.例如"NEW.count <> OLD.count".
但我想要的是:如果"NEW <> OLD"则运行触发器
一个例子:
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 |
+------+------+
Run Code Online (Sandbox Code Playgroud)
关键是,有一个更新,但没有任何改变.但无论如何都触发了触发器.恕我直言应该有一种方式它没有.
我知道我可以使用
如果NOW.b <> OLD.b
对于这个例子.
想象一下有一个变化列的大桌子.您必须比较每一列,如果数据库发生更改,则必须调整触发器.并且比较硬编码的行的每一列并不"感觉"好:)
加成
正如你所看到的那样
匹配的行数:1已更改:0警告:0
MySQL知道该行没有改变.但它不会与触发器分享这些知识.像"AFTER REAL UPDATE"之类的触发器或类似的东西会很酷.
Inc*_*nca 70
作为一种变通方法,您可以使用时间戳(旧的和新的)进行检查,但是当行没有更改时,不会更新时间戳.(可能这是混淆的原因?因为那个也被称为'更新'但是在没有发生变化时不执行)一秒内的变化将不会执行触发器的那一部分,但在某些情况下可能没问题(就像你有一个拒绝快速变化的应用程序一样.)
例如,而不是
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
Run Code Online (Sandbox Code Playgroud)
你可以用
IF NEW.ts <> OLD.ts
THEN
INSERT INTO bar (a, b) VALUES(NEW.a, NEW.b) ;
END IF
Run Code Online (Sandbox Code Playgroud)
然后,每次更新方案时都不必更改触发器(问题中提到的问题.)
编辑:添加完整的示例
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)
Run Code Online (Sandbox Code Playgroud)
由于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 |
+-------+-----------+------+-----+-------------------+-----------------------------+
Run Code Online (Sandbox Code Playgroud)
Den*_*rdy 15
想象一下有一个变化列的大桌子.您必须比较每一列,如果数据库发生更改,则必须调整触发器.并且比较每行硬编码并不"感觉"好:)
是的,但这是继续进行的方式.
作为旁注,在更新之前进行先发制人检查也是一种好习惯:
UPDATE foo SET b = 3 WHERE a=3 and b <> 3;
Run Code Online (Sandbox Code Playgroud)
在您的示例中,这将使它更新(并因此覆盖)两行而不是三行.
Wax*_*age 12
我不能发表评论,所以请注意,如果你的列支持NULL值,那么OLD.x <> NEW.x就不够了,因为
SELECT IF(1<>NULL,1,0)
返回0与...相同
NULL<>NULL 1<>NULL 0<>NULL 'AAA'<>NULL
Run Code Online (Sandbox Code Playgroud)
因此它不会跟踪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))
Run Code Online (Sandbox Code Playgroud)
您可以通过使用NULL-safe equals运算符<=>比较每个字段然后使用否定结果来完成此操作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 ;
Run Code Online (Sandbox Code Playgroud)