3

我正在创建一个数据库,这是我第一次与他们合作。

我有一个关于一对一通信的问题。我正在创建一个代表某项运动团队的表格。

我希望每支球队都只有一个比赛场地,每支球队都有自己的场地。

竞技场将有许多属性(名称、座位、票价等),所以我会为他们创建一个表,即使我已经读过创建一对一关系的最佳实践是将所有属性放在一个表中.

因为竞技场和团队是两个如此不同的概念,所以我会将它们分成两个表格,如下所示:

Club
--------------------
ClubID (PK)
ClubName
.
.
.
ArenaID (FK)


Arena
--------------------
ArenaID (PK)
ClubID (FK)
ArenaName
Seats
TicketPrice

此外,我认为 ArenaID 不是 AutoIncremental,而是与 ClubID 相等,因为我会在创建俱乐部时自动创建 Arena。合理吗?

我想很好地理解这种类型的关系,因为它也将用于其他情况(例如,每支球队总会有一名具有他技能的助理教练)。

谢谢大家!

编辑:

@Kevin Bowersox 我只是在说明游戏将如何进行,感谢您的回答。

@Peter Gluck您应该将竞技场视为俱乐部的一个属性,但它有其他自己的属性,我想将它与俱乐部表分开以更好地组织数据库。如果一个俱乐部想要“建造”另一个竞技场,它只需改变实际竞技场的名称,它就可以改变座位数和票价。但是保存在数据库中的“对象”将保持不变。只有一些列将被更改。

@Branko Dimitrijevic 感谢您的详细回答。我想问你一些问题,以便更好地理解你所说的(我现在开始使用数据库)。在第一个示例中,您没有为两个表使用两个键,或者一个主键,另一个是外键,但您使用唯一的键,即同时主键和外键,这是正确的吗?这两种选择有什么区别?然后,你说如果我没有太多的团队,我把所有都放在一个桌子上会更好,但我希望我会有几万(或几十万)队。在这种情况下,这是一个足够大的数字来拆分表吗?我这样说是因为我在这个表中不仅仅是这个一对一的关系,所以如果我把所有这些一对一的关系放在同一个表中,它会非常非常大。

感谢大家的建议,现在我将尝试使用带有两个表和两个不同 ID 键的方法。但我将在其他表格中使用 ypercube 建议的方法!

4

4 回答 4

4

我喜欢拆分这些表的想法。它将使您的对象模型工作得更好。

但是,关于:

此外,我认为 ArenaID 不是 AutoIncremental,而是与 ClubID 相等,因为我会在创建俱乐部时自动创建 Arena。合理吗?

我会将 ArenaID 保留为它自己的自动整数。如果俱乐部更换场地会发生什么?这只是可能导致这些密钥不同步的一种情况。我有一种感觉,随着时间的推移,您将使用这种方法手动调整其中一些键。保持你的关系数据库,就是这样,RELATIONAL,不要依赖环境。

于 2013-01-04T20:52:55.460 回答
1

在支持可延迟约束的 DBMS 上,可以使用循环外键实现真正的“1 对 1”:

CREATE TABLE Club (
    ClubId INT PRIMARY KEY
    -- Other fields...
);

CREATE TABLE Arena (
    ClubId INT PRIMARY KEY REFERENCES Club (ClubId)
    -- Other fields...
);

ALTER TABLE Club ADD FOREIGN KEY (ClubId)
    REFERENCES Arena (ClubId)
    DEFERRABLE INITIALLY DEFERRED;

直到事务结束才强制执行FK Club -> Arena,因此您可以插入Club然后Arena不会遇到先有鸡还是先有蛋的问题。


不幸的是,您将无法在 MySQL 中实现真正的“1 对 1”关系,因为它不支持延迟约束,因此在插入新数据时您将无法解决先有鸡还是先有蛋的问题真正的“1 对 1”所必需的圆形 FK 的存在。

您甚至无法真正依赖您提出的解决方案,因为 MySQL 不强制执行 CHECK 约束,因此您无法强制执行 and 之间的相等ClubIdArenaId。可能您能做的最好的事情就是依靠您的应用程序逻辑来执行它,从而使您面临潜在的错误。

使用备用键(即 UNIQUE 约束)也无济于事,因为:

CREATE TABLE Club (
    ClubId INT PRIMARY KEY,
    ArenaId INT UNIQUE
    -- Other fields...
);

CREATE TABLE Arena (
    ArenaId INT PRIMARY KEY,
    ClubId INT NOT NULL UNIQUE REFERENCES Club (ClubId)
    -- Other fields...
);

ALTER TABLE Club ADD FOREIGN KEY (ArenaId)
    REFERENCES Arena (ArenaId);

INSERT INTO Club (ClubId) VALUES (1);
INSERT INTO Club (ClubId) VALUES (2);
INSERT INTO Club (ClubId) VALUES (3);

INSERT INTO Arena (ArenaId, ClubId) VALUES (1, 2);
INSERT INTO Arena (ArenaId, ClubId) VALUES (2, 3);
INSERT INTO Arena (ArenaId, ClubId) VALUES (3, 1);

UPDATE Club SET ArenaId = 2 WHERE ClubId = 1;
UPDATE Club SET ArenaId = 3 WHERE ClubId = 2;
UPDATE Club SET ArenaId = 1 WHERE ClubId = 3;

现在您有一个ClubId = 1指向一个竞技场 ( ArenaId = 2) 的俱乐部 ( ) 指向另一个俱乐部 ( ClubId = 3)!ETC...

即使它有效,聚集表中的二级索引也可能很昂贵,并且所有 InnoDB 表都是聚集的


您提出的“垂直分区”通常是为了更好地利用缓存,当查询的字段子集比另一个多得多并且行数很大时。即使您有这种特殊的查询模式,我怀疑您是否有足够的团队来解决缓存问题。你真的应该重新考虑,把所有东西都放在一张桌子上。

于 2013-01-05T01:01:16.913 回答
0

你似乎很好地理解了这些概念。您确实在寻找适当的考虑因素来推动您的决定。只需确保您所做的假设是正确的,并且将保持正确..(即,永远不会允许担任 B 队助理教练的个人成为另一支球队的助理教练吗?如果这些问题的答案是总是是的,对于所有关系,它们确实可以一对一地建模,(实际上,您可以将它们全部作为单个团队表的属性而不会丢失功能),但要小心,很少有假设就像这实际上是真实的,并且随着应用程序和数据模型的发展注定会随着时间的推移保持真实......

假设这确实是一对一的,那么我考虑将它们分成单独的表的唯一原因是为了提高性能。假设一组(较小的)属性每秒被访问数百次,而大部分属性相当稳定并且访问频率要低得多。然后,您可以将更频繁访问的属性放在一个更小更窄的表中,以获得更好的读写性能。纯粹为了逻辑或组织美学而分离属性不是我会做的事情......

于 2013-01-04T20:55:17.193 回答
0

你说(在评论中):“一个团队不能改变竞技场,因为它可以改变它的竞技场的名称和维度,所以会有永远的一对一关系。”

永远不要说永远永远。需求变化。

我同意@Peter Gluck 的评论,即“本质上不同的对象应该有自己的键,无论它们的关系如何”

根据前两个观察,我会为所有三个对象/实体保留单独的表ClubArenaClubPlaysInArena。您(当前)对俱乐部和竞技场之间 1-1 关系的限制将受到两个Unique限制条件的保留,如果要求发生变化,可以取消这些限制条件。设计中无需进一步更改:

Club
--------------------
ClubID (PK)
ClubName
.
.

Arena
--------------------
ArenaID (PK)
ArenaName
Seats
.
.

ClubPlaysInArena
--------------------
ClubID  (PK, UQ1, FK1)
ArenaID (PK, UQ2, FK2)
.
.

奖励点:您可以在数据库中存储没有关联俱乐部的新建竞技场(以及没有竞技场的俱乐部)。

于 2013-01-06T01:35:17.940 回答