如何将几个锦标赛/括号类型建模到SQL数据库中?

时间:2015-10-14 23:22:30

标签: mysql sql database-design

我想建模一个数据库来存储几种类型的比赛数据(不同类型的模式:单轮,双轮,联赛,联赛+季后赛,输家......)。

也许,这个项目将成为一种商标:www​​.challonge.com

我的问题是:如何在sql-relationship数据库中创建一个模型来存储所有类型的锦标赛?

我无法想象如何做这项工作。有很多不同的表,但所有表都与一个属性相关:tournamentType ...

我可以存储tournamentType字段并使用此字段在查询中选择适当的表吗?

谢谢你

6 个答案:

答案 0 :(得分:6)

我可以理解为什么你在为这个建模而苦苦挣扎。这很困难的一个关键原因是object relational impendance-mismatch。虽然我是SQL的忠实粉丝,但它是一种非常强大的能够组织数据的方式,它的一个缺点 - 以及NoSQL存在的原因 - 是因为SQL与面向对象编程不同。当您描述具有不同匹配的联赛时,很容易以对象形式对其进行描绘:Match对象由League_MatchRound_MatchKnockout_Match等扩展。这些Match个对象包含两个Team个对象。 Team可以扩展为WinnerLoser ...

但这不是SQL数据库的工作方式。

所以让我们将其转化为关系:

  

我想建模一个数据库来存储几种类型的比赛数据(不同类型的模式:单轮,双轮,联赛,联赛+季后赛,输家......)。

  • 锦标赛和“模式”是一对多(1:n)的关系。
  • 每场锦标赛都有很多球队,每支球队都可以参加很多锦标赛(n:n)。
  • 每支球队都有很多比赛,每场比赛都有两支球队(n:n)。
  • 每场比赛都有多场比赛,但每场比赛只属于一场比赛(1:n)。

这里缺少的部分难以定义为普遍关系? - 在轮次中,每场未来的比赛都有两支球队。 - 在淘汰赛中,根据初始球队的数量,每场未来的比赛都有指数但不断缩小的选择。

您可以在数据库层中定义它,也可以在应用程序层中定义它。如果您的目标是保持参照完整性(这是我使用SQL数据库的关键原因之一),那么您将希望将其保留在数据库中。

另一种看待这种情况的方法:我发现,当我考虑最终结果时,通过将其视为可与之交互的JSON(或数组,如果您愿意),我最容易设计数据库。

让我们看看一些示例对象:

赛:

[
    {
        name: "team A",
        schedule: [
            {
                date: "11/1/15",
                vs: "team B",
                score1: 2,
                score2: 4
             },
             {
                date: "11/15/15",
                vs: "team C",
             }
        ]
    }
],
[
   //more teams
]

正如我所看到的,除了淘汰赛之外,这种方法效果很好,在淘汰赛之前,你实际上并不知道哪支球队将会参加其他球队。这证实了我的感觉,即我们将创建一个Tournament类的后代来处理特定类型的锦标赛。

因此,我建议使用以下列的三个表:

Tournament
- id (int, PK)
- tournament_name
- tournament_type

Team
- id (int, PK)
- team_name (varchar, not null)
# Any other team columns you want.

Match
- id (int, PK, autoincrement)
- date (int)
- team_a_score (int, null)
- team_b_score (int, null)
- status (either future, past, or live)
- tournament_id (int, Foreign Key)

Match_Round
- match_id (int, not null, foreign key to match.id)
- team_a_id (int, not null, foreign key to team.id)
- team_b_id (int, not null, foreign key to team.id)

Match_Knockout
- match_id (int, not null, foreign key to match.id)
- winner__a_of (match_id, not null, foreign key to match.id)
- winner_b_of (match_id, not null, foreign key to match.id)

您已在此模型中使用了子表。这样做的好处是淘汰赛和圆/联赛非常不同,你对他们的看法不同。缺点是你要增加额外的复杂性,你将不得不处理。这可能有点烦人,但根据我的经验,试图避免它只会增加更多的麻烦,并使其可扩展性降低。

现在我将回到参照完整性。这种设置的挑战是理论上,当Match_RoundMatch_Knockout只属于一个时,您可以拥有值TRIGGERMatch_Round。为了防止这种情况,我会使用Match_Knockout s。基本上,在INSERTtournament_type表格上设置一个触发器,如果​​var canvas = new fabric.Canvas(); var image = new Image(); canvas.add( image ); image.center() // Optional if you wish the object centered on canvas image.setCoords(); 不可接受,则会阻止{{1}}。

尽管设置起来有点麻烦,但它仍然可以很容易地转换为对象,同时仍然保持参照完整性。

答案 1 :(得分:3)

您可以创建表来保存锦标赛类型,联赛类型,季后赛类型,并有一个计划表,显示偶数名称及其锦标赛类型,然后使用该关系来检索有关该锦标赛的信息。注意,这不是MySQL,这是更通用的SQL语言:

CREATE TABLE tournTypes (
ID int autoincrement primary key,
leagueId int constraint foreign key references leagueTypes.ID,
playoffId int constraint foreign key references playoffTypes.ID
--...other attributes would necessitate more tables
)

CREATE TABLE leagueTypes(
ID int autoincrement primary key,
noOfTeams int,
noOfDivisions int,
interDivPlay bit -- e.g. a flag indicating if teams in different divisions would play
)

CREATE TABLE playoffTypes(
ID int autoincrement primary key,
noOfTeams int,
isDoubleElim bit -- e.g. flag if it is double elimination
)

CREATE TABLE Schedule(
ID int autoincrement primary key,
Name text,
startDate datetime,
endDate datetime,
tournId int constraint foreign key references tournTypes.ID
)

填充表格......

INSERT INTO tournTypes VALUES
(1,2),
(1,3),
(2,3),
(3,1)

INSERT INTO leagueTypes VALUES
(16,2,0), -- 16 teams, 2 divisions, teams only play within own division
(8,1,0),
(28,4,1)

INSERT INTO playoffTypes VALUES
(8,0), -- 8 teams, single elimination
(4,0),
(8,1)

INSERT INTO Schedule VALUES
('Champions league','2015-12-10','2016-02-10',1),
('Rec league','2015-11-30','2016-03-04-,2)

获取比赛信息......

SELECT Name
,startDate
,endDate
,l.noOfTeams as LeagueSize
,p.noOfTeams as PlayoffTeams
,case p.doubleElim when 0 then 'Single' when 1 then 'Double' end as Elimination
FROM Schedule s
INNER JOIN tournTypes t
ON s.tournId = t.ID
INNER JOIN leagueTypes l
ON t.leagueId = l.ID
INNER JOIN playoffTypes p
ON t.playoffId = p.ID

答案 2 :(得分:2)

使数据模型变得比它们需要的复杂得多。您描述的很多内容都是业务逻辑,实际上可以通过完美的数据模型来回答。大多数锦标赛逻辑应该以编程语言在数据模型之外捕获,例如mysql函数,Java,Python,C#等。真正的数据模型应该是全部的静态"您需要的数据,而不是任何移动部件。我建议数据模型为:

METADATA TABLES

League_Type:

  • 编号
  • 描述
  • Playoff_Rounds
  • Resolve_Losing_Teams
  • Max_Number_of_Teams
  • Min_Number_of_Teams
  • Number_Of_Games_In_Season
  • 任何其他"设置"你想......

Game_Type:

  • 编号
  • League_Type_Id(fk to League_Type)
  • Game_Type_Name(例如常规赛,季后赛,锦标赛)

DATA TABLES

联赛:

  • 编号
  • League_Type_Id(fk to League_Type)
  • LEAGUE_NAME

小组:

  • 编号
  • League_Id(fk to League_Type)
  • TEAM_NAME

游戏:

  • 编号
  • League_Id(fk to League_Type)
  • Game_Type_Id(fk to Game_Type)
  • Home_Team_Id(fk to Team)
  • Visiting_Team_Id(fk to Team)
  • Week_of_season
  • HOME_TEAM_SCORE
  • Visiting_Team_Score
  • Winning_Team(家庭或访客)

从数据模型的角度来看,这应该是你真正需要的。程序代码应该处理以下内容:

  • 根据随机计划创建游戏
  • 在游戏桌中更新分数并赢得团队
  • 根据联赛设置表中本赛季的比赛数量增加来创建季后赛比赛。
  • 根据每支球队有多少场比赛,在季后赛中设置对决。
  • 在赛季开始之前强制联盟中的球队数量介于Min_Number_of_Teams和Max_Number_of_Teams之间。

您还可能希望根据这些表创建一些视图,以便为最终用户创建一些其他有意义的信息:

  • 团队的胜利/损失(基于加入Game表的Team表)
  • 根据之前所有球队的赢/输视图,目前的球队排名
  • 团队主场胜利
  • 团队的胜利
  • 你心中想要的任何其他东西!

最后的想法

您不想做任何会重复存储在数据库中的数据的事情。一个很好的例子就是为季后赛和常规赛创建一个单独的桌子。大多数列都是重复的,因为几乎所有的所有功能和存储在两个表之间的数据是相同的。创建两个表将破坏规范化规则。您的数据结构越紧凑,越简单,您编写的程序代码就越少,维护数据库就越容易。

答案 3 :(得分:0)

这看起来像是一个泛化/专业化问题。我将以一般方式回答如何做到这一点,因为您没有详细说明您实际需要的实体。

假设您有一个实体Vehicle(取代您的锦标赛)和专业化TrainCar。所有车辆都有maxSpeed属性,火车有numberOfWagonsCartrunkCapacity

要对此进行建模,您有以下几种选择:

(1)将它们全部合并到一个表中

您可以使用maxSpeed,numberOfWagons和trunkCapacity列创建一个表Vehicle。您添加了另一个字段vehicleType来区分列车和汽车,并且您可能想要Id

对于任何具体的Verhicle,某些列将始终为null。

(2)使用单独的超级/子表

或者,您可以使用VehicleId创建一个表maxSpeed,并为TrainCar创建表,这些表只包含额外的属性,即numberOfWagons和trunkCapacity(以及Id)。

在这种情况下,创建一辆新车需要两个插件,一个在车辆表中,一个在车载表中。要选择汽车,您必须加入汽车和汽车,除非您只对其车辆属性感兴趣。

虽然这种方法比(1)更复杂,但它有一些好处

  • 你不会有那么多的空列。像"汽车的trunkCapacity不得为空的"可以很容易地表现出来。
  • 您可以通过添加新表并且不更改任何现有表来添加新的Verhicle类型。

在两者之间转换

  • 从(2)开始,您仍然可以获得"合并"通过创建视图查看所有车辆的视图(如(1)中所示)。此视图将是多个选择的并集,其中每个选择将一个特化(Train或Car)与Vehicle连接,并为无法从特化中检索的属性添加常量空列,因此union中的所有选择返回相同数量的列

  • 从(1)起,您可以通过从“车辆”表中选择特定车辆类型,并仅选择与该车辆类型相关的列,为列车和汽车创建单独的视图。

(3)两者的混合

您可以将最突出的属性合并到一个表中,并将更多异域属性放入额外的表中。

提醒

必须注意不要过分概括。通常最好只将Cat模型化为Cat。在面向对象的编程中,泛化(" Superclasses")是值得珍惜的。节省了代码重复,但列重复并不像代码重复那么糟糕。请记住,您只是建模数据而不是行为。在OO-land中,一般化也经常过度。

答案 4 :(得分:0)

我真的不明白这个模型的复杂性。我们来看看:

enter image description here

你需要:

  • 锦标赛表(唯一定义每场锦标赛 - 联赛,杯赛等)
  • 锦标赛类型,时段和阶段(定义每个锦标赛特征)
  • 比赛(每场比赛的每场比赛,包括主场和客队以及最终比分)
    • 比赛类型(链接到锦标赛阶段)
    • 球队,球员和球员(如果您打算添加该级别的信息,最后2个可选)

以此为例:

  • 锦标赛:Premier Leage
  • 锦标赛类型:联赛
  • tournament_period:2015-2016
  • tournament_phase:第20轮
  • 对决:切尔西VS利物浦
  • matchup_type:第二站
  • score_visitor:2
  • score_local:0

  • 锦标赛:冠军联赛
  • 锦标赛类型:锦标赛
  • tournament_period:2015-2016
  • tournament_phase:第二轮
  • matchup_type:第一站
  • 对决:切尔西VS巴塞罗那
  • score_visitor:5
  • score_local:0

我做得非常快,所以关系和列可能不对,但我猜你有一个起点。

希望它有所帮助!

此致

答案 5 :(得分:-2)

不是一个优雅的解决方案,但您可以执行以下操作:

创建一个表,其中包含给定锦标赛的键值对属性。每场锦标赛都将存储在多个行中。

CREATE TABLE TOURNAMENT {
    TOURNAMENT_TYPE VARCHAR2(100) NOT NULL,
    TOURNAMENT_NAME VARCHAR2(100) NOT NULL,
    ATTRIBUTE_NAME VARCHAR2(100) NOT NULL,
    ATTRIBUTE_VALUE VARCHAR2(100) NOT NULL
};

e.g。

TOURNAMENT_TYPE,TOURNAMENT_NAME,ATTRIBUTE_NAME,ATTRIBUTE_VALUE
Volleyball,My volleyball tournament,team_member_1,Josie
Volleyball,My volleyball tournament,team_member_2,Ralph
Volleyball,My volleyball tournament,Rounds Per Game,12
Soccer,My volleyball tournament,team_member_1,Jim
Soccer,My soccer tournament,team_member_2,Emma
Soccer,My soccer tournament,Tournament Duration,20