首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >仅当行已更改时,MySQL触发器after update

仅当行已更改时,MySQL触发器after update
EN

Stack Overflow用户
提问于 2011-06-10 00:43:24
回答 8查看 190.2K关注 0票数 70

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

但是我想要这样的东西:如果"NEW <> OLD“就运行触发器

举个例子:

代码语言:javascript
复制
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,则为

。b <> OLD.b

在这个例子中。

但是想像一下,有一个大的表,其中的列在变化。您必须比较每一列,如果数据库发生更改,您必须调整触发器。硬编码的行的每一列都进行比较并不“感觉”好:)

添加

正如你在线路上看到的

匹配的

行数:1已更改:0警告:0

MySQL知道这条线没有改变。但它并不与触发器共享这些知识。像“真正更新后”这样的触发器或类似这样的东西将会很酷。

EN

回答 8

Stack Overflow用户

回答已采纳

发布于 2011-06-10 04:32:32

作为一种解决办法,您可以使用时间戳(旧的和新的)来检查,当行没有更改时,时间戳不会更新。(这可能是造成混乱的原因?因为它也被称为“on update”,但在没有发生更改时不会执行),因此一秒内的更改将不会执行触发器的该部分,但在某些情况下,这可能会很好(例如,当您有一个应用程序拒绝快速更改时)。

例如,而不是

代码语言:javascript
复制
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

你可以使用

代码语言:javascript
复制
IF NEW.ts <> OLD.ts 
THEN  
  INSERT INTO bar (a, b) VALUES(NEW.a, NEW.b) ;
END IF

这样,您就不必每次更新方案时都更改触发器(您在问题中提到的问题)。

编辑:添加了完整的示例

代码语言:javascript
复制
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

代码语言:javascript
复制
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 |
+-------+-----------+------+-----+-------------------+-----------------------------+
票数 75
EN

Stack Overflow用户

发布于 2011-06-10 00:52:10

,但想象一下一个包含不断变化的列的大表。您必须比较每一列,如果数据库发生更改,您必须调整触发器。而且,比较每一行硬编码的代码也不是很“好”:)

是啊,但这才是继续下去的方式。

顺便说一句,在更新之前进行先发制人的检查也是一种好的做法:

代码语言:javascript
复制
UPDATE foo SET b = 3 WHERE a=3 and b <> 3;

在您的示例中,这将使它更新(并因此覆盖)两行,而不是三行。

票数 16
EN

Stack Overflow用户

发布于 2014-01-13 19:59:23

我无法评论,所以请注意,如果您的列支持空值,则OLD.x<>NEW.x是不够的,因为

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

返回0,与相同

代码语言:javascript
复制
NULL<>NULL 1<>NULL 0<>NULL 'AAA'<>NULL

因此它不会跟踪从NULL到NULL的更改

在这个场景中,正确的方法是

代码语言:javascript
复制
((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))
票数 15
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6296313

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档