一尘不染

数据库触发器对跨表完整性约束是否安全?

sql

我建议使用触发器来检查跨表完整性约束,以回答此问题。在评论中建议它可能会引起问题:

进行跨行检查的触发器在大多数数据库上很少起作用…因为它们无法从其他事务中读取未提交的行

不过,我没有找到任何支持该说法的消息来源。官方
文档未提及任何内容。我发现的其他问题在本文的SO上进行了介绍它主要批评潜在的隐藏复杂性,因为一眼就看不到触发器。但是,即使是评分最高的答案也承认其用于完整性问题。

所以我的问题是: 数据库触发器对跨表完整性约束是否安全? 具体来说,以下解决方案行得通吗?


总结一下原来的问题。我们有桌子

  • Player - PlayerID, PlayerName
  • Bet - BetID, BetName
  • plays_in - BetID, PlayerID

约束条件是BetName和PlayerID的组合应该唯一。建议触发器的定义:

CREATE TRIGGER check_bet_name BEFORE INSERT ON plays_in 
  FOR EACH ROW BEGIN
      DECLARE bet_exists INT DEFAULT 0;
      DECLARE msg VARCHAR(255);

      SELECT 1 INTO bet_exists 
        FROM Bet AS b1
        WHERE b1.BetID = NEW.BetID
          AND EXISTS (SELECT * 
            FROM plays_in AS p JOIN Bet AS b2 USING (BetID)
            WHERE p.PlayerID = NEW.PlayerID AND b2.BetName = b1.BetName
          )
        LIMIT 1;

      IF bet_exists THEN
        SET msg = "Bet name already exists...";
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = msg;
      END IF;
  END//

阅读 188

收藏
2021-03-10

共1个答案

一尘不染

答案是 触发器不安全

事实证明,触发器确实看不到其他事务中未提交的更改,并且没有错误地通过。这样可以证明

交易1:

START TRANSACTION;
INSERT INTO plays_in (BetID, PlayerID) VALUES (1,1); -- query A

交易2:

START TRANSACTION;
INSERT INTO plays_in (BetID, PlayerID) VALUES (1,2); -- query B; in conflict with A, but passses

两项交易:

COMMIT;

plays_in即使A和B在单个事务中执行,现在也会包含两个插入的记录,触发器将引发错误。

整个示例资源可以在这里找到

2021-03-10