MySQL:定义多个表的唯一性

时间:2013-06-02 00:20:01

标签: mysql database-design constraints

我开始开发一个用户可以创建投注的应用程序。给出以下方案

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中解决的问题吗?

3 个答案:

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