5

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

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

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

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


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

  • 玩家 - PlayerID, PlayerName
  • 投注 - BetID、BetName
  • play_in - 投注ID,玩家ID

约束是 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//
4

2 回答 2

5

答案是触发器不安全

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

交易一:

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

可以在此处获得整个示例源

于 2013-06-02T08:50:10.797 回答
1

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

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

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

于 2015-06-08T18:41:10.340 回答