有多种方法可以对这些数据进行建模,例如单表、多表、硬编码列、动态方式等。需要一种方式来表示每个队伍的种子和位置,每个比赛的结果(以及可能的得分),以及在锦标赛中谁与谁比赛以及在什么阶段进行比赛。
在三月疯狂的精神下,我认为这是一个好问题。有一些显而易见的答案,但主要目标是看看您能够回答这个问题的所有不同方式。最好的方法可能因使用的语言或具体工作方式而异,但请尽量保持答案与数据库无关、与编程语言无关,并且比较高级别。如果有人对更好的措辞或定义方式有建议,请在评论中让我知道。
自然的倾向是按比赛的顺序查看括号。你从外向内阅读传统图表。但让我们换个角度想一想。每场比赛都是由两个队伍之间进行的。一个胜利,另一个失败。
现在,除此之外,还有更多的内容。特定一组比赛的获胜者将在另一场比赛中对抗。因此,比赛本身也存在关系,而不考虑谁在这些比赛中打球。也就是说,在每场比赛中(除了第一轮),对决的队伍是之前两场比赛的获胜者。
因此,您可能会注意到,每场比赛都有两个“子游戏”来确定谁将参加该游戏。这听起来很像二叉树:每个根节点最多有两个子节点。如果您知道谁赢得了每场比赛,那么您可以轻松地确定“父”游戏中的队伍。
因此,为了设计一个模型数据库,您实际上只需要两个实体:Team
和 Game
。每个Game
具有与其他Game
相关的两个外键。名称并不重要,但我们会将它们建模为单独的键,以强制要求每个比赛不得有超过两个前置比赛。让我们称它们为leftGame
和rightGame
,以保持二叉树术语。同样,我们应该有一个名为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
当然,您可以向实体添加所有其他所需的信息:位置、分数、结果(如果比赛因弃权或其他非正常情况而赢得)。
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;这个团队将与另一个比赛的获胜者相匹配。填写比赛表,直到没有更多的获胜团队为止。
由于您没有指定关系型数据库,我将采用与众不同的方式,并选择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的技术能力,欢迎任何知道更多的人提供评论。
4个表:
团队(团队,地区,种子)
用户(用户ID,电子邮件,等等)
括号(括号ID,用户ID,积分)
选择(括号ID,比赛ID,团队,积分)
每个人提交的括号将在选择表中有63行。
每场比赛结束后,您需要更新选择表以计算个人选择的得分。此表中的积分字段对于尚未进行的比赛将为null,对于错误的选择将为0,对于正确的选择将为正数。GameId只是一个键,用于标识该用户的括号中此选择的位置(例如:East_Round2_Game2,FinalFour_Game1)。
括号表中的积分列可以在每次更新选择表后进行更新,以便包含该括号的总积分。最常查看的内容将是排名,不希望每次有人想查看排行榜时都重新计算。
您无需保留所有实际进行的比赛或其结果的表,只需在每场比赛后更新选择表即可。您甚至可以通过查看选择表中的积分列来突出显示正确/错误的选择。
提议的ER图 http://img257.imageshack.us/img257/1464/ncaaer.jpg
我们只需要知道一个团队的名称和种子值。因此,我们需要一个"团队"表来存储种子值。唯一的候选键是团队名称,所以我们将使用它作为主键,以保持简单。NCAA团队名称在一个锦标赛期间不太可能改变或包含重复项,所以它应该是一个足够的键。
一个"比赛"表可以用来将团队配对到每个比赛中。外键(FK1,FK2)指向"团队"表,确保团队存在,并且这些值上的主键确保团队只能相互匹配一次。
从"比赛"表到"团队"表的外键(FK4)将记录获胜者。逻辑上,获胜者必须是参与比赛的两个团队之一。对主键的检查约束可以确保这一点。
一旦比赛结果确定,就可以从团队表中检索获胜者的种子,以便与其他获胜者进行比较,以确定随后的比赛。在执行此操作时,可以将FK(FK3)写入确定比赛,以描述锦标赛的进展情况(尽管可以随时推导出这些数据)。
我还对每个比赛的游戏进行了建模。游戏是根据其所属比赛和按照比赛期间发生顺序的序列号来识别的。游戏具有来自团队表的获胜者(FK2)。得分也可以记录在此表中。
我在所有的数据库中使用相同的模式。
t
--------
1 guid PK
2 guid FK
3 bit
然后在我的代码中:
select [2],[3] from t where [1] = @1
@1 是我正在获取的数据的 ID。然后,如果 [2] 不为空,我会再次选择,将 @1 设置为 [2]。
这使得对你发布的情况进行建模变得非常容易。