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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据库触发器对于跨表完整性约束是否安全?相关的知识,希望对你有一定的参考价值。

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

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

但是,我没有找到任何支持索赔的消息来源。 Official documentation没有提及任何事情。我发现的其他问题包括here on SO - 它主要批评潜在的隐藏复杂性,因为触发器在第一眼看不到。甚至连highest rated answer都承认他们使用完整性问题。

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


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

  • Player - PlayerID,PlayerName
  • 投注 - BetID,BetName
  • plays_in - BetID,PlayerID

约束BetName和玩家ID的组合应该是唯一的。建议触发器的定义:

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//
答案

答案是触发器不安全。

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

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

整个示例来源可以获得qazxsw poi

另一答案

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

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

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

以上是关于数据库触发器对于跨表完整性约束是否安全?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以同时拥有外键约束和保持参照完整性的触发器?

数据库系统原理之数据库安全与保护

pl/sql 触发器完整性约束问题

数据库知识

Redshift 中如何保持参照完整性?

数据库完整性