我建议使用触发器来检查跨表完整性约束,以回答此问题。在评论中建议它可能会引起问题:
进行跨行检查的触发器在大多数数据库上很少起作用…因为它们无法从其他事务中读取未提交的行
不过,我没有找到任何支持该说法的消息来源。官方 文档未提及任何内容。我发现的其他问题在本文的SO上进行了介绍它主要批评潜在的隐藏复杂性,因为一眼就看不到触发器。但是,即使是评分最高的答案也承认其用于完整性问题。
所以我的问题是: 数据库触发器对跨表完整性约束是否安全? 具体来说,以下解决方案行得通吗?
总结一下原来的问题。我们有桌子
约束条件是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//
答案是 触发器不安全 。
事实证明,触发器确实看不到其他事务中未提交的更改,并且没有错误地通过。这样可以证明
交易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在单个事务中执行,现在也会包含两个插入的记录,触发器将引发错误。
plays_in
整个示例资源可以在这里找到