我开始开发一个用户可以创建投注的应用程序。给出以下方案
TABLE Player
PlayerID PRIMARY KEY
PlayerName
(...)
TABLE Bet
BetID PRIMARY KEY
BetName
(...)
TABLE plays_in
BetID
PlayerID
PRIMARY KEY(BetID, PlayerID)
FOREIGN KEY BetID
FOREIGN KEY PlayerID
以任何方式定义(BetName,PlayerID)的唯一性,以便Bet可以多次使用相同的名称,但对于玩家只能使用一次?这意味着某个玩家只能参加一次名为“MyFirstBet”的投注?我不想将赌注名称定义为主键或唯一,因为任何其他不会与上述玩家进行此赌注的玩家也应该能够将他的赌注命名为“MyFirstBet”。如果可能的话,我想避免为此创建一个额外的表。这是在代码中而不是在DBMS中解决的问题吗?
答案 0 :(得分:4)
将“BetName”移至“plays_in”表。
TABLE plays_in
BetName
PlayerID
PRIMARY KEY(BetName, PlayerID)
FOREIGN KEY PlayerID
然后放下表“Bet”。
答案 1 :(得分:1)
标准SQL不支持此模型 - 此问题是BetName不是密钥,并且(当前)不是候选密钥的一部分。
解决此问题的一种方法保持关系不变并确保参照完整性是将一个BetName列添加到PlaysIn,然后使用PlayIn FK(BetId,BetName),因此有一个FK结束候选键而不仅仅是代理PK 。接下来,添加PlaysIn UX(BetName,PlayerId)以强制使用唯一的名称/播放器。基本上,它通过Surrogate 和适当的复合键来约束关系。这有点icky,因为没有用于RI的“重复数据”(在复合PK之外)。
TABLE Bet
PK BetID
BetName
TABLE PlaysIn
BetID
PlayerID
BetName -- must set
PK (BetID, PlayerID)
FK Bet(BetID, BetName)
UX (PlayerID, BetName)
我推荐的另一种方法,虽然它确实会改变关系,但是将PlayId 移出的PlaysIn并将其保留为Bet。然后播放 - >投注 - >播放器。用户体验也可以升级为PK,BetID可以被删除,使其与上述类似。
TABLE Player
PK PlayerID
PlayerName
TABLE Bet
-- Note: If PlaysIn needs PlayerID as well, use PK(PlayerId, BetName)
-- and adjust the FK in PlaysIn
PK BetID
PlayerID
BetName
UX (PlayerID, BetName)
TABLE PlaysIn
PK PlayID -- If you're gonna use Surrogates, be consistent
BetID
FK (BetID) -- Access to Player via Bet
-- other things for a "Play"
当然,TRIGGERS可以“全力以赴”,但不能直接代表关系。如果您信任DAL,代码也可以在插入/更新时一丝不苟。
我会考虑按照第二种方法更改模型。
答案 2 :(得分:0)
编辑:正如LoztInSpact建议的那样,此解决方案确实不起作用。问题在于事务和触发器没有看到未经修改的更改。请参阅here。
试试database triggers。您可以检查您的状况,然后发出错误信号或将插入的值更改为NULL或某些特殊值。
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//
另见this answer。