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

时间:2013-06-02 08:23:34

标签: mysql sql triggers transactions

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

  

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

但是,我没有找到任何支持该声明的消息来源。 Official documentation没有提及任何内容。我发现的其他问题已被涵盖here on SO - 它主要批评潜在的隐藏复杂性,因为触发器在第一眼看不到。即使highest rated answer承认他们使用完整性问题也是如此。

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


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

  • 播放器 - 播放器ID,播放器名称
  • 投注 - 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//

2 个答案:

答案 0 :(得分:5)

答案是触发器不安全

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

交易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在单个事务中执行,触发器也会抛出错误。

可以查看整个示例来源here

答案 1 :(得分:1)

这可能取决于哪个数据库以及您编写逻辑的程度。

早期版本的infomodeler / visiomodeler支持一些非常神秘和复杂的引用完整性形式,并提供了在几个数据库上实现它们的代码。早期版本的Sybase / sql server不支持声明性参照完整性,因此所有逻辑都在触发器中成功实现。

我不会将反例的一个实现失败作为权威。