如何强制实现一对多关系

5
我正在开发一个在线多人游戏,并有一个SQL服务器问题。
假设这个游戏只允许两名玩家参加。当游戏创建时,创建者被添加为第一个用户。
此时,两个用户可能同时尝试加入游戏。其中一个用户应该被阻止。
我的数据库模式如下:
tbGame - contains a list of all games. PrimaryKey is GameId
tbPlayers - contains a list of all registered users. PrimaryKey is PlayerId
tbPlayersInGame - contains a list of all players in each game. Foreign key 
                  relations to tbGame and tbPlayers.

我觉得我需要两件事情:

  1. 一种基于GameId锁定tbPlayersInGame的方法。这将用于向游戏中添加玩家时使用。从我所读的内容来看,似乎适合使用键范围锁(在GameId上)。
  2. 一种强制实施1对2关系的方法,以便尝试添加第3个玩家会失败。
2个回答

1

与其建立1对n的关系,您可以设置表格具有两列 - 可能标记为host_playervisitor_player,或者等效的必要内容(例如在象棋中 - blackwhite)。这样做的好处是将有趣的事情最小化,并显示关系的自然状态。

当然,这仅适用于应该有永久有限数量的玩家的情况 - 对于大多数桌游实例,这通常可以正常工作....

如果您试图制作一个上限可变的游戏(出于任何原因),则可以使用以下语句“检测”可用的空闲插槽:

INSERT INTO game_players (game_id, player_id) 
SELECT VALUES (:GAME_ID, :PLAYER_ID)
WHERE :MAX_PLAYER_COUNT > (SELECT COUNT(*)
                           FROM game_players
                           WHERE game_id = :GAME_ID)

如果您收到100的错误代码(“未选择/更新任何行”),则表示玩家列表已满。您可能需要添加其他条件(以防止玩家加入两次),但是这个概念仍然有效。

谢谢回复。我应该更清楚地表达,游戏可以有2-8个不同数量的玩家。因此,我避免将玩家添加到游戏表中。我认为在SQL Server中无法向插入语句添加where子句,如果我错了,请纠正我。 - Buurin
对不起,你是正确的,不能只添加一个WHERE子句 - 我的意思是将其作为SELECT的一部分...进行编辑。 - Clockwork-Muse

1
一些建议:
1)在尝试写入tbPlayersInGame表之前,先进行SELECT以确保游戏未满,然后再进行INSERT INTO操作。将此SELECT和INSERT INTO操作包装在一个事务中,并将事务隔离级别设置为可串行化。
2)不要有单独的tbPlayersInGame表,而是在tbGame中有两个字段:Player1Id和Player2Id。

最终我采取了这种方法。查询大致如下:'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE BEGIN TRANSACTION IF (SELECT COUNT(*) FROM PlayersInGame WHERE GameId = @GameId < SELECT MaxPlayers FROM Game WHERE GameId = @GameId) BEGIN INSERT INTO PlayersInGame ... COMMIT TRAN RETURN 0 ENDROLLBACK TRAN RETURN -1' - Buurin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接