当前位置: 首页 > 面试题库 >

仅当行已更改时,MySQL才会在更新后触发

穆宏胜
2023-03-14
问题内容

仅在真正更改数据的情况下,才有可能使用“更新后”触发器。我知道“新旧”。但是使用它们时,我只能比较列。例如“ 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 |
+------+------+

关键是,有一个更新,但是 什么都没有改变 。但是无论如何,触发器都在运行。恕我直言,应该有一个没有的方法。

我知道我可以使用

如果现在b <> OLD.b

对于这个例子。

但是,想象一下一个带有变化列的大桌子。您必须比较每列,如果数据库发生更改,则必须调整触发器。并且比较硬编码的行的每一列都是“感觉”不好:)

加成

如您所见

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

MySQL知道这条线没有改变。但是它不会与触发器共享这些知识。像“ AFTER REAL 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 |
+-------+-----------+------+-----+-------------------+-----------------------------+


 类似资料:
  • 问题内容: 由于声誉的限制,这是对先前问题的回答的后续问题。 但是想象一下一个有变化的列的大桌子。您必须比较每列,如果数据库发生更改,则必须调整触发器。而且比较硬编码的每一行都没有“感觉”好:) 是的,但这就是进行的方式。 附带说明一下,在更新之前先检查一下也是一种好习惯: 在您的示例中,这将使其更新(从而覆盖)两行而不是三行。 我想知道在处理NULL值时是否有更有效的方法来测试每个字段的更改。

  • 问题内容: 如何告诉Jenkins / Hudson仅针对Git树中特定项目的更改触发构建? 问题答案: Git插件有一个选项(排除的区域),可使用正则表达式根据提交中的文件是否与排除的区域正则表达式匹配来确定是否跳过构建。 不幸的是,当前的Git插件目前没有“包含区域”功能(1.15)。但是,有人在GitHub上发布了可在Jenkins和Hudson上运行的补丁,这些补丁实现了所需的功能。 构建

  • 创建一个在PHP服务器端无法理解的小管理员行更新程序。我希望sql查询只更新页面上已更改的$u POST字段,这样它就不会循环所有100条左右的记录。 我的做法如下: 1) jQuery侦听已更改的输入字段,然后为已更新的用户字段分配名称属性 2) PHP检测已设置更新的用户字段。 3) 在提交重新加载时,PHP检测到该输入,并按行ID更新SQL。如何确保PHP知道哪个输入字段指向哪个行ID? j

  • 问题内容: 我在SQL Server中运行合并。在我的更新中,我只想更新值已更改的行。版本行在每次更新时都会递增。下面是一个示例: 现在,如果我只想更新行,从而增加版本,则仅在名称更改的情况下。 问题答案: 可以有。另外,无需更新。 如果Last_Name或First_Name可为空,则例如在比较trg.Last_Name <> src.Last_Name时,需要注意值。

  • 只有当新项目的日期比现有项目更近时,我才想更新DynamoDB中的项目。目前,我正在查询现有项目,在代码中进行比较,然后写入数据库。我想知道是否有办法让DynamoDB为我进行检查。我已经研究过使用预期,但是它的比较运算符需要引入一个参数,这违背了目的,因为这意味着无论如何都必须查询现有项目。 我和8Java一起工作。

  • 我是新来的Laravel我想做的只是如下: 我的表单中有一些字段,如标题、描述。 标题字段在数据库中是唯一的。 这就是我所做的来更新我的价值观。 但这将导致错误(该值已经存在),因为我的标题字段是唯一的。 我只想在标题值更改时更新标题,否则更新相同的值,但更新其他字段。谢谢