MariaDB 触发器在同一张表上执行 UPDATE 和 INSERT

Posted

技术标签:

【中文标题】MariaDB 触发器在同一张表上执行 UPDATE 和 INSERT【英文标题】:MariaDB trigger to perform an UPDATE and then an INSERT on the same table 【发布时间】:2021-12-20 17:43:36 【问题描述】:

我目前有一个account 表和一个account_audit 表(所有代码都可以在小提琴here 上找到)。

我有两个触发器,它们根据插入到帐户表中的数据将记录插入到 account_audit 表中。

1 触发器是一个 ON INSERT 触发器,它只插入这些值 - 工作正常。

所以,在我的审计表上插入 2 条记录后 - 我在两个表中都有两条记录,如下所示。

SELECT * FROM account;

给予

acct_id acct_name   acct_balance    tax_rate    acct_opened
      1 Bill            100.00          0.10     2019-11-01
      2 Ben            1000.00          0.10     2019-11-01

SELECT * FROM account_audit;

给予

acct_id txn_id  acct_name   acct_balance    tax_rate    valid_from  valid_to
      1      1       Bill         100.00        0.10    2021-11-07  2038-01-19
      2      2        Ben        1000.00        0.10    2021-11-07  2038-01-19

一切都好 - 2038 年是 MariaDB 无穷大 - 至少在临时表中!

但是,我的UPDATE触发器如下:

CREATE TRIGGER testtrigger_upd 
AFTER UPDATE ON account
  FOR EACH ROW BEGIN
    UPDATE account_audit SET valid_to = NOW() 
    WHERE ((valid_to = '2038-01-19') AND (OLD.acct_name = NEW.acct_name));
    INSERT INTO 
      account_audit (acct_id, acct_name, acct_balance, tax_rate) 
      VALUES 
      (
        OLD.acct_id, 
        OLD.acct_name, 
        NEW.acct_balance, 
        OLD.tax_rate
        
        -- valid_from - NOW()!
        -- valid_to DEFAULTs to '2038-01-19' which is what we want for updates to balance 
      );
END;

因此,当我更新帐户余额时,我希望 account_audit 跟踪反映这一点,然后将最新(插入之前)余额记录转到 valid_to(今天)和新记录 @987654331 @ 今天到 valid_to 在 2038 年(MariaDB 无限?)。

问题是当我运行这个语句时

UPDATE account 
SET acct_balance = acct_balance + 50 WHERE acct_name = 'Bill';  -- name is UNIQUE

account_audit 表中的记录变为:

acct_id txn_id  acct_name   acct_balance    tax_rate    valid_from  valid_to
      1      1       Bill         100.00    0.10        2021-11-07  2021-11-07
      1      3       Bill         150.00    0.10        2021-11-07  2038-01-19
      2      2        Ben        1000.00    0.10        2021-11-07  2021-11-07

您可以看到 Bill 的记录已插入,但问题是 Ben 的记录也已更新为今天插入的日期 - 我显然只希望 Bill 发生这种情况!

我的触发器中有WHERE ((valid_to = '2038-01-19') AND (OLD.acct_name = NEW.acct_name));,我尝试了很多其他方法-WHERE OLD.acct_id = NEW.acct_id,我尝试将更新更改为使用acct_id 而不是名称-没有任何效果。我尝试交换OLD。和新的。订购 - 在更新之前或之后尝试 - 我真的疯了!

如何重写触发器以更新其帐户的人而不是所有帐户的 account_audit 表 - 也就是说,仅更新 Bill 的帐户而不是 Ben 的帐户? p>

我知道临时表功能 - 我不想使用它,因为这是为了研究,我必须使用触发器!

小提琴是here。

【问题讨论】:

【参考方案1】:

那是因为您的 where 子句适用于所有行

因此更改更新查询以检查表

CREATE TRIGGER testtrigger_upd 
AFTER UPDATE ON account
  FOR EACH ROW BEGIN
    UPDATE account_audit SET valid_to = NOW() 
    WHERE ((valid_to = '2038-01-19') AND (acct_name = NEW.acct_name));
    INSERT INTO 
      account_audit (acct_id, acct_name, acct_balance, tax_rate) 
      VALUES 
      (
        OLD.acct_id, 
        OLD.acct_name, 
        NEW.acct_balance, 
        OLD.tax_rate
        
        -- valid_from - NOW()!
        -- valid_to DEFAULTs to '2038-01-19' which is what we want for updates to balance 
      );
END;
UPDATE account 
SET acct_balance = acct_balance + 50 WHERE acct_name = 'Bill';
SELECT * FROM account_audit
帐户 ID | txn_id |账户名 |账户余额 |税率 |有效来自 |有效 ------: | -----: | :-------- | ------------: | --------: | :--------- | :--------- 1 | 1 |比尔 | 100.00 | 0 | 2021-11-07 | 2021-11-07 2 | 2 |本 | 1000.00 | 0 | 2021-11-07 | 2038-01-19 1 | |比尔 | 150.00 | 0 | |

db小提琴here

【讨论】:

【参考方案2】:

在我看来,where 子句

WHERE ((valid_to = '2038-01-19') AND (OLD.acct_name = NEW.acct_name));

对 Ben 和 Bill 都有效。所以它会为他们两个更新。

一种解决方案可能是将其稍微更改为:

WHERE ((valid_to = '2038-01-19') AND (acct_name = OLD.acct_name));

【讨论】:

WHERE ((valid_to = '2038-01-19') AND (acct_name = OLD.acct_name)); - 那got it!如果您愿意将其写下来,我会很乐意将其标记为正确并投赞成票。简单的错误,但我是 mysql/MariaDB 触发器的初学者... 非常感谢!我现在已经更新了。这是一个很好的问题。当您身处险境时,很难透过树木看到森林。 我将您的答案标记为正确 - 也尝试过投票 - 没有 15 个代表。然而,但它说我的投票被记录了 - 所以希望你能得到更多的分数!

以上是关于MariaDB 触发器在同一张表上执行 UPDATE 和 INSERT的主要内容,如果未能解决你的问题,请参考以下文章

更新时在同一张表上执行触发器

我想在同一张表上创建更多触发器

瞬间截断和插入同一张表

表上触发器导致慢查询

使用 X 和 U 锁在同一张表上发生死锁

MySQL INSERT 在同一张表上使用带有 COUNT() 的子查询