我正在创建一个数据库,这是我第一次与他们合作。
我有一个关于一对一通信的问题。我正在创建一个表格,代表某个运动的团队。
我希望每支球队只有一个竞技场比赛,每支球队都有自己的竞技场。
竞技场将有许多属性(名称,座位,门票价格等)所以我会为他们创建一个表,即使我已经读过创建一对一关系的最佳实践将是全部一张桌子。
因为竞技场和团队是两个如此不同的概念,我会用这样的方式将它们分成两个表:
Club
--------------------
ClubID (PK)
ClubName
.
.
.
ArenaID (FK)
Arena
--------------------
ArenaID (PK)
ClubID (FK)
ArenaName
Seats
TicketPrice
另外我想把ArenaID放在AutoIncremental上,但是把它等同于ClubID,因为我会在创建一个Club时自动创建Arena。 这是合理的吗?
我想要理解这种类型的关系,因为它也将用于其他情况(例如,每个团队总是会有一名助理教练与他的技能)。
感谢所有人!
编辑:
@Kevin Bowersox我只是说明游戏将如何,我感谢你的回答。@Peter Gluck您应该将竞技场视为俱乐部的一个属性,但它有其他属性,我想将其与俱乐部表分开以组织更好的数据库。如果俱乐部想要“建立”另一个竞技场,它只是改变了实际竞技场的名称,它可以改变座位数和票价。但保存在数据库中的“对象”将保持不变。只会更改一些列。
@Branko Dimitrijevic感谢您的详细解答。我想问你一些问题,以便更好地理解你所说的内容(我现在开始使用数据库)。 在第一个示例中,对于两个表都不使用两个键,或者,一个是主键,另一个是外键,但是您只使用同时是主键和外键的键,这是对的吗?这两个选择有什么区别? 然后,你说如果我没有多少团队,如果我把所有人都放在单人桌上会更好,但我希望我将拥有数十(或几百)个团队。在这种情况下这是一个足够大的数字来分割表?我这样说是因为我在这个表中只有这个一对一的关系,所以如果我将所有这些一对一的关系放在同一个表中,它将会非常大..
感谢大家的建议,现在我将尝试使用两个表和两个不同ID键的方法。 但我会在其他表中使用ypercube建议的方法!
答案 0 :(得分:4)
我喜欢拆分这些表格的想法。它将使您的对象模型更好地工作。
但是,关于:
另外我想把ArenaID放在AutoIncremental上,但是要放 它等于ClubID,因为我会自动创建竞技场 什么时候创建一个俱乐部。这是合理的吗?
我会将ArenaID作为自己的自动整数。如果俱乐部切换竞技场会发生什么?这只是一种可能导致这些密钥不同步的情况。我有一种感觉,随着时间的推移,你将使用这种方法手动调整其中一些键。保留你的关系数据库,就是这样,RELATIONAL并且不依赖于环境。
答案 1 :(得分: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约束,因此您无法强制ClubId
和ArenaId
之间的相等。您可以做的最好的事情就是依靠您的应用程序逻辑来强制执行该操作,从而为您提供潜在的错误。
使用备用键(即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
)!等...
即使它起作用,群集表中的二级索引也很昂贵,all InnoDB tables are clustered。
您建议的“垂直分区”通常是为了更好地利用缓存,当一个字段的子集查询比另一个更多并且行数很大时。即使你有这种特殊的查询模式,我也怀疑你是否有足够的团队来解决缓存问题。你应该重新考虑并把所有东西都放在一张桌子里。
答案 2 :(得分:0)
您似乎很好地理解了这些概念。您确实在考虑推动决策的适当考虑因素。只要确保你所做的假设是正确的,并且保持正确。(即,作为B队教练的个人是否永远不会被允许成为另一支球队的助理教练?如果这些问题的答案是总是是的,对于所有关系,那么它们确实可以被建模为一对一,(事实上,你可以将它们全部作为单个团队表的属性而不会丢失功能),但要小心,很少是假设这样的确如此,随着应用程序和数据模型的发展,注定会随着时间的推移保持真实......
假设这确实是一对一的,那么我考虑将它们分成单独的表的唯一原因是性能。假设一个(较小的)属性集每秒被访问数百次,而大部分属性相当稳定并且访问频率低得多。然后,您可以将更频繁访问的属性放在更小和更窄的表中,以获得更好的读写性能。纯粹为逻辑或组织美学分离属性并不是我想做的事情......
答案 3 :(得分:0)
你说(在评论中):“一个团队无法改变竞技场,因为它可以改变竞技场的名称和维度,因此将会有一对一的永久关系。”
永远不要说从不或永远。要求变更。
我同意@Peter Gluck的评论“本质上不同的对象应该有自己的密钥,无论他们的关系如何”
根据之前的两个观点,我将为所有三个对象/实体保留单独的表:Club
,Arena
和ClubPlaysInArena
。您对俱乐部和竞技场之间1-1关系的(当前)限制将由两个Unique
限制保留,如果需求发生变化,可以取消这些限制。设计中不需要进一步更改:
Club
--------------------
ClubID (PK)
ClubName
.
.
Arena
--------------------
ArenaID (PK)
ArenaName
Seats
.
.
ClubPlaysInArena
--------------------
ClubID (PK, UQ1, FK1)
ArenaID (PK, UQ2, FK2)
.
.
加分点:您可以在数据库中存储没有关联俱乐部(以及没有竞技场的俱乐部)的新建场馆。