1

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

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) 定义唯一性,以便下注可以多次使用相同的名称,但对于玩家只能使用一次?意思是某个玩家只能参与一次名为“MyFirstBet”的投注?我不想将下注名称定义为主键或唯一名称,因为任何其他不会与上述玩家下注的玩家也应该能够将他的下注命名为“MyFirstBet”一次。如果可能的话,我也想避免为此创建一个额外的表。这是在代码中而不是在 DBMS 中解决的问题吗?

4

3 回答 3

4

将“BetName”移动到“plays_in”表中。

TABLE plays_in
  BetName
  PlayerID
  PRIMARY KEY(BetName, PlayerID)
  FOREIGN KEY PlayerID

然后删除表“Bet”。

于 2013-06-02T00:58:04.257 回答
1

标准 SQL 不支持此模型 - 这个问题是 BetName 不是键,并且(当前)不考虑候选键的一部分。

解决此问题的一种方法是保持关系原样并确保引用完整性是向 PlaysIn 添加一列 BetName ,然后使用 PlayIn FK(BetId, BetName) ,因此在Candidate Key上存在 FK而不仅仅是Surrogate PK。接下来,添加 PlaysIn UX(BetName, PlayerId) 以强制使用唯一名称/玩家。基本上,它通过代理适当的复合键来约束关系。这有点令人讨厌,因为没有用于 RI 的“重复数据”(在 Compound PK 之外)。

TABLE Bet
  PK BetID
  BetName 

TABLE PlaysIn
  BetID
  PlayerID
  BetName -- must set
  PK (BetID, PlayerID)
  FK Bet(BetID, BetName)
  UX (PlayerID, BetName)

我推荐的另一种方法虽然确实会改变关系,但将 PlayerIdPlaysIn 中移出并保留在 Bet 中。然后 PlaysIn -> 投注 -> 玩家。UX 也可以升级为 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。

根据第二种方法,我会考虑更改模型。

于 2013-06-02T00:35:05.753 回答
0

编辑:正如 LoztInSpact 所建议的,这个解决方案真的不起作用。问题在于事务和触发器没有看到未提交的更改。见这里


尝试数据库触发器。您可以检查您的条件,然后发出错误信号或将插入的值更改为 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//

另请参阅此答案

于 2013-06-02T00:40:35.663 回答