最佳架构来表示NCAA篮球比赛的抽签表

20
什么是表示NCAA男子篮球比赛图的最佳数据库模式?如果您不熟悉,请参考此链接:http://www.cbssports.com/collegebasketball/mayhem/brackets/viewable_men
有多种方法可以对这些数据进行建模,例如单表、多表、硬编码列、动态方式等。需要一种方式来表示每个队伍的种子和位置,每个比赛的结果(以及可能的得分),以及在锦标赛中谁与谁比赛以及在什么阶段进行比赛。
在三月疯狂的精神下,我认为这是一个好问题。有一些显而易见的答案,但主要目标是看看您能够回答这个问题的所有不同方式。最好的方法可能因使用的语言或具体工作方式而异,但请尽量保持答案与数据库无关、与编程语言无关,并且比较高级别。如果有人对更好的措辞或定义方式有建议,请在评论中让我知道。

由于没有一个“唯一正确”的答案,我建议将其设为社区维基问题。 - George Stocker
我不知道是否存在唯一正确的答案。我同意答案可能是主观的,但我认为对于大多数应用程序来说,会有一个明显的最佳解决方案,你不觉得吗?如果事实证明确实如此,我会将其作为 CW 条目。 - Ryan Guill
要明确的是,我并不是在开发一个括号应用程序。我的意图是利用这个作为一种方式来找出在规范化数据库中建模真实世界应用程序的最佳方法,而这并没有一个直截了当的答案。 - Ryan Guill
8个回答

12

自然的倾向是按比赛的顺序查看括号。你从外向内阅读传统图表。但让我们换个角度想一想。每场比赛都是由两个队伍之间进行的。一个胜利,另一个失败。

现在,除此之外,还有更多的内容。特定一组比赛的获胜者将在另一场比赛中对抗。因此,比赛本身也存在关系,而不考虑谁在这些比赛中打球。也就是说,在每场比赛中(除了第一轮),对决的队伍是之前两场比赛的获胜者。

因此,您可能会注意到,每场比赛都有两个“子游戏”来确定谁将参加该游戏。这听起来很像二叉树:每个根节点最多有两个子节点。如果您知道谁赢得了每场比赛,那么您可以轻松地确定“父”游戏中的队伍。

因此,为了设计一个模型数据库,您实际上只需要两个实体:TeamGame。每个Game具有与其他Game相关的两个外键。名称并不重要,但我们会将它们建模为单独的键,以强制要求每个比赛不得有超过两个前置比赛。让我们称它们为leftGamerightGame,以保持二叉树术语。同样,我们应该有一个名为parentGame的键来跟踪反向关系。

此外,正如我之前所指出的,您可以通过查看谁赢得了前两场比赛来轻松确定参加每场比赛的队伍。因此,您实际上只需要跟踪每场比赛的胜者。因此,将Game实体赋予winner外键到Team表。

现在,问题在于种子的括号。也就是说,模拟第一轮比赛的比赛安排。您可以通过为整个比赛中的每个团队建立一个Game来模拟这种情况,其中该团队是winner并且没有前置比赛。

因此,整体架构将是:

Game:
    winner: Team
    leftGame: Game
    rightGame: Game
    parentGame: Game
    other attributes as you see fit

Team:
    name
    other attributes as you see fit

当然,您可以向实体添加所有其他所需的信息:位置、分数、结果(如果比赛因弃权或其他非正常情况而赢得)。


6
对于一个关系型数据库,我认为最简单的方法,同时仍然足够灵活以适应大多数情况,是执行以下操作:
  • Teams 包含 [team-id (PK)], [name], [region-id (FK 到 Regions)], [initial-seed]。每个团队都有一个条目。(区域表是一个微不足道的代码表,只有四个条目,分别对应 NCAA 的四个区域,这里没有列出。)

  • Participants 包含 [game-id (FK 到 Games)], [team-id (FK 到 Teams)], [score (可为空)], [outcome][score] 可为空,以反映一个团队可能放弃比赛。通常每场比赛会有两个参与者。

  • Games 包含 [game-id (PK)], [date], [location]。要查找哪些团队参加了比赛,请在 Participants 表中查找相应的 game-id。(请记住,如果有人退出或被取消资格,则可能会有超过两个团队。)

为了设置初始的比赛表,将适当的种子匹配在一起。当比赛进行时,请注意哪个团队在特定比赛中具有 outcome = Winner;这个团队将与另一个比赛的获胜者相匹配。填写比赛表,直到没有更多的获胜团队为止。


我喜欢这个,但是从数据上看,你如何知道如何匹配团队?例如,您需要知道它们是否位于中西部、西部、东部或南部,您会仅仅依靠编程来知道1号种子队伍与16号种子队伍比赛吗?但是您如何确切地知道谁是胜者的对手呢? - Ryan Guill
1
是的,我认为像“1号种子对阵16号种子”这样的规则应该被编码到业务逻辑中,而不是存储在数据库中。但我忘了考虑不同地区的情况,我会加以修改。 - John Feminella
更具体地说,规则是如果最高编号的种子是N,其中N是2的幂,则编号为k的种子在初始比赛中与编号为N-k+1的种子对战。(例如,种子k = 4在16个种子的比赛中与种子16-4 + 1 = 13对战。) - John Feminella
如果没有,则将编号为i = 1 ... N/2的数字分配给每个比赛对手。在下一轮比赛中,如果i是奇数,则比赛i的获胜者将与i + 1的获胜者比赛,如果i是偶数,则比赛i的获胜者将与i - 1的获胜者比赛。 - John Feminella
在某个时候,您将不得不在解决方案中包含代码。您似乎正在询问如何将匹配逻辑放入数据库中。您不需要这样做。将逻辑放在代码中,在数据库中存储足够的数据来驱动您的代码。问题解决了,我的赏金在哪里。 - Peter Seale
显示剩余4条评论

2

由于您没有指定关系型数据库,我将采用与众不同的方式,并选择CouchDB方法,因为我上周末正在阅读有关它的内容。以下是我设计的代表游戏的文档结构。

{
  "round" : 1, //The final would be round 5, and I guess Alabama St. vs. Morehead would be 0
  "location" : "Dayton, OH",
  "division": "South",
  "teams" : ["UNC", "Radford"]  //A feature of Couch is that fields like teams don't need a fixed nuber of columns.
  "winner" : "UNC"  //Showing my bias
}

一个更加有趣或完整的应用程序可能也会将关于团队、排名等数据存储在某个地方。约翰的方法似乎很好地涵盖了这方面。对于我Couch的技术能力,欢迎任何知道更多的人提供评论。


2
我创建了一个包含以下表的小型系统:
游戏:GameId、TournId、RoundId、Sequence、日期、VisitorId、VisitorScore、HomeId、HomeScore、WinnerId、WinnerGameId、WinnerHome(位)
预测:PredId、UserId、GameId、PredVisitorId、PredHomeId、PredWinnerId
回合:RoundId、TournId、RoundNum、Heading1、Heading2
球队:TeamId、TournId、TeamName、Seed、MoreInfo、Url
锦标赛:TournId、TournDesc
用户:TournId、UserName WinnerGameId将游戏胜者与他们的下一场比赛连接起来。WinnerHome表示胜者在下一场比赛中是主队还是客队。除此之外,我认为这些都相当容易理解。

1
在跟踪大量不同的括号预测时:您可以使用67个位来跟踪每场比赛的结果。(即,锦标赛中的每场六十七场比赛都由一个位表示,1 =“A队获胜”,0 =“B队获胜”)。要显示任何给定的括号,您可以使用一个非常简单的函数将67个位映射到UI上。该函数知道团队名称及其初始位置,然后在跟踪'位板'时跟踪它们在括号中的移动。

1

4个表:

团队(团队,地区,种子)

用户(用户ID,电子邮件,等等)

括号(括号ID,用户ID,积分)

选择(括号ID,比赛ID,团队,积分)

每个人提交的括号将在选择表中有63行。
每场比赛结束后,您需要更新选择表以计算个人选择的得分。此表中的积分字段对于尚未进行的比赛将为null,对于错误的选择将为0,对于正确的选择将为正数。GameId只是一个键,用于标识该用户的括号中此选择的位置(例如:East_Round2_Game2,FinalFour_Game1)。

括号表中的积分列可以在每次更新选择表后进行更新,以便包含该括号的总积分。最常查看的内容将是排名,不希望每次有人想查看排行榜时都重新计算。

您无需保留所有实际进行的比赛或其结果的表,只需在每场比赛后更新选择表即可。您甚至可以通过查看选择表中的积分列来突出显示正确/错误的选择。


1

提议的模型

提议的ER图 http://img257.imageshack.us/img257/1464/ncaaer.jpg

团队表

我们只需要知道一个团队的名称和种子值。因此,我们需要一个"团队"表来存储种子值。唯一的候选键是团队名称,所以我们将使用它作为主键,以保持简单。NCAA团队名称在一个锦标赛期间不太可能改变或包含重复项,所以它应该是一个足够的键。

比赛表

一个"比赛"表可以用来将团队配对到每个比赛中。外键(FK1,FK2)指向"团队"表,确保团队存在,并且这些值上的主键确保团队只能相互匹配一次。

从"比赛"表到"团队"表的外键(FK4)将记录获胜者。逻辑上,获胜者必须是参与比赛的两个团队之一。对主键的检查约束可以确保这一点。

一旦比赛结果确定,就可以从团队表中检索获胜者的种子,以便与其他获胜者进行比较,以确定随后的比赛。在执行此操作时,可以将FK(FK3)写入确定比赛,以描述锦标赛的进展情况(尽管可以随时推导出这些数据)。

游戏表

我还对每个比赛的游戏进行了建模。游戏是根据其所属比赛和按照比赛期间发生顺序的序列号来识别的。游戏具有来自团队表的获胜者(FK2)。得分也可以记录在此表中。


1
你的名字是Blake Taylor,你正在回答一个关于NCAA篮球锦标赛的问题吗?除非这不是巧合 :) - Michael Myers

-4

我在所有的数据库中使用相同的模式。

t
--------
1 guid PK
2 guid FK
3 bit

然后在我的代码中:

select [2],[3] from t where [1] = @1

@1 是我正在获取的数据的 ID。然后,如果 [2] 不为空,我会再次选择,将 @1 设置为 [2]。

这使得对你发布的情况进行建模变得非常容易。


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