SQL查询获取随机未使用的组合

时间:2012-04-22 21:36:50

标签: sql database

背景

我想创建一个可以运行1对1比赛的锦标赛的数据库。它需要跟踪谁赢得和输掉每场比赛以及有关该比赛的任何评论以及随机决定下一场独特比赛。

规则:

有x个玩家。每个玩家最终将玩一次其他玩家,实际上涵盖了所有可能的独特玩家组合。

数据库表(包含示例数据):

DECLARE @Players TABLE (
    ID INT PRIMARY KEY IDENTITY,
    Name VARCHAR(50)
)

ID Name  
-- ----- 
1  Alex  
2  Bob   
3  Chris 
4  Dave 

DECLARE @Matches TABLE (
    ID INT PRIMARY KEY IDENTITY,
    WinnerId INT,
    LoserId INT
)

ID WinnerId LoserId 
-- -------- ------- 
1  1        2       
2  4        2       
3  3        1    

DECLARE @Comments TABLE (
    ID INT PRIMARY KEY IDENTITY,
    MatchId INT,
    Comment VARCHAR(MAX)
)

ID MatchId Comment                        
-- ------- ------------------------------ 
1  2       That was a close one.          
2  3       I did not expect that outcome. 

问题:

  • 如何有效地查询以获得尚未发生的单个随机匹配?

主要问题是玩家的数量会随着时间的推移而增长。现在在我的示例数据中,我只有4个玩家,留下6个可能的匹配。

Alex,Bob
Alex,Chris
Alex,Dave
Bob,Chris
Bob,Dave
Chris,Dave

这足够小,只需继续抓取2个与玩家身份相对应的随机数,然后检查匹配表是否已经发生过匹配。如果它有:再获得2个并重复该过程。如果没有,那么将它用作下一场比赛。但是如果我有10,000名玩家,可能会有49995000次对战,那就太慢了。

有人能指出我正确的方向以获得更有效的查询吗?我愿意接受数据库设计的变化,如果这样做有助于提高效率。

4 个答案:

答案 0 :(得分:1)

如果你在每个可能的配对和已经播放的配对之间进行外连接,那么过滤掉已经播放的配对,你就会留下尚未播放的配对。选择一个随机的是一个微不足道的排序案例:

SELECT p1.Name, p2.Name FROM
  Players p1
  JOIN Players p2 ON (
    p1.ID < p2.ID
  )
  LEFT JOIN Matches ON (
       (WinnerId = p1.ID AND LoserId = p2.ID)
    OR (WinnerId = p2.ID AND LoserId = p1.ID)
  )
WHERE Matches.ID IS NULL
ORDER BY RAND()
LIMIT 1;

修改

如下面ypercube所述,上述LIMIT语法是MySQL特有的。您可能需要使用适当的SQL实现语法 - 如果需要,请告诉我们它是什么以及有人可以提供建议。我知道在Microsoft SQL Server中使用TOP和Oracle ROWNUM,但是否则您的Google搜索可能与我的一样好。 :)

答案 1 :(得分:0)

虽然数据集很大,但只要返回一个键,使用limit键就会停止其​​他处理。一种可能性是使用下面的查询来返回下一个匹配。

SELECT * FROM Players p1, Players p2 WHERE p1.ID <> p2.ID AND (p1.ID, p2.ID) NOT IN (Select WinnerID, LoserID FROM Matches) AND (p2.ID, p1.ID) NOT IN (Select WinnerID, LoserID FROM Matches) LIMIT 1

答案 2 :(得分:0)

我想知道为什么你需要随机挑选2名球员。如何预先生成可能匹配的完整列表,然后添加WinnerId列?对于下一场比赛,只需选择没有设置WinnerId的第一行。

答案 3 :(得分:0)

对于您的问题,您希望A)以随机顺序考虑玩家B)的所有2元素子集。

对于A,其他答案建议使用具有各种条件的SQL连接。如果您真的需要处理10,000个玩家,那么数据库密集度较低的解决方案可能是使用有效的组合生成算法。我找到了之前的答案,列出了一些来自TAOCP vol。 4 here。对于2元素子集的情况,在字典序列中对播放器ID的简单双嵌套循环将是正常的:

for player_a in 1..num_players:
  for player_b in player_a+1..num_players:
    handle a vs. b

对于B部分,您可以使用第二个表将玩家1..n映射到整数1..n的混洗。保持这种混乱的映射,直到你完成锦标赛过程。您可以使用Knuth-Fisher-Yates shuffle

要跟踪此问题实例中的位置,您可能希望定期将组合生成器的状态保存到数据库中。这可能比单独从原始表中确定序列中的位置更快。

正如你所提到的,以这种方式处理对手的10,000名球员会导致将近五千万场比赛。您可能会考虑一种锦标赛结构,不需要每个玩家与其他玩家竞争。例如,如果A击败B和B击败C,那么您可能不必考虑A是否胜过C.如果适用于您的场景,那种快捷方式可以节省大量时间。