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

Mif*_*eet 4 mysql sql triggers transactions

我建议使用触发器检查交叉表完整性约束来回答这个问题.评论中建议它可能会导致问题:

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

但是,我没有找到任何支持索赔的消息来源.官方 文档没有提到任何内容.我发现的其他问题在这里也有所涉及- 它主要批评潜在的隐藏复杂性,因为触发器在第一眼看不到.即使是评分最高的答案也会承认他们对完整性问题的使用.

所以我的问题是:数据库触发器对于跨表完整性约束是否安全?特别是,下面的解决方案是否有效?


总结原始问题.我们有桌子

  • Player - PlayerID,PlayerName
  • 投注 - 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//
Run Code Online (Sandbox Code Playgroud)

Mif*_*eet 5

答案是触发器不安全.

事实证明,触发器确实没有看到在其他事务中完成的未经修改的更改并且没有错误地传递.它可以这样证明

交易1:

START TRANSACTION;
INSERT INTO plays_in (BetID, PlayerID) VALUES (1,1); -- query A
Run Code Online (Sandbox Code Playgroud)

交易2:

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

两笔交易:

COMMIT;
Run Code Online (Sandbox Code Playgroud)

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

这里可以看到整个示例源