我应该在什么情况下使用一对一关系?

126

很抱歉提出这样的初学者问题,但是在数据库中使用一对一关系是否有真正的必要?您可以在一个表格内实现所有必要的字段。即使数据变得非常大,您也可以在SELECT语句中枚举所需的列名,而不是使用SELECT *。那么什么情况下需要进行这种分离呢?

14个回答

148

1到0..1

  • "超类和子类之间的1到0..1"是实现继承中"所有类分别存储在不同表格"策略的一部分。

  • "1到0..1"可以使用单个表格表示,其中"0..1"部分由可为空的字段覆盖。然而,如果关系大多是"1到0",只有少数"1到1"行,将"0..1"部分拆分成一个单独的表格可能会节省一些存储空间(和缓存性能)优势。一些数据库比其他数据库更加节俭地存储NULL值,因此这种策略变得可行的"截止点"可以差异很大。

1到1

  • 真正的“一对一”垂直分区数据,这可能会影响缓存。数据库通常在页面级别实现缓存,而不是在单个字段的级别上,因此即使您只从行中选择了几个字段,通常也会缓存该行所属的整个页面。如果一行非常宽,而所选的字段相对较窄,则最终将缓存许多您实际上不需要的信息。在这种情况下,垂直分区数据可能很有用,因此仅缓存更窄、更频繁使用的部分或行,这样更多的行可以适合缓存,使缓存有效地变得“更大”。

  • 垂直分区的另一个用途是改变锁定行为:数据库通常无法锁定单个字段,只能锁定整行。通过拆分行,您允许锁定仅发生在其一半上。

  • 触发器通常也是特定于表的。虽然理论上您可以只有一个表,并使触发器忽略“错误的一半”行,但某些数据库可能会对触发器的操作加以限制,这可能使这种做法不切实际。例如,Oracle 不允许您修改变异表——通过拥有单独的表,只有其中一个可能是变异的,因此您仍然可以从触发器修改另一个表。

  • 单独的表可能允许更精细的安全性。

在大多数情况下,这些考虑因素都不相关,因此您应该将“1对1”表合并为单个表。

另请参见:为什么在数据库设计中使用1对1关系?


46

我的个人看法。

我在一个工作场所工作,我们都在一个大型应用程序中进行开发,一切都是模块化的。例如,我们有一个users表,我们有一个模块为用户添加Facebook详细信息,另一个模块为用户添加Twitter详细信息。我们可以决定卸载其中一个模块,并从我们的应用程序中删除所有相关功能。在这种情况下,每个模块都会向全局users表添加自己的表,具有1:1的关系,如下所示:

create table users ( id int primary key, ...);
create table users_fbdata ( id int primary key, ..., constraint users foreign key ...)
create table users_twdata ( id int primary key, ..., constraint users foreign key ...)

31

如果在一个表格中放置两个一对一的表格,可能会出现语义问题。例如,如果每个设备都有一个遥控器,将设备和遥控器及它们的属性放在同一个表格中并不是很好。你甚至需要花时间去确定某个属性属于设备还是遥控器。

在某些情况下,您的一半列可能会长期为空,或者永远不会被填充。例如,汽车可以有一个配有大量特征的拖车,也可能没有。这样就会有许多未使用的属性。

如果您的表格有20个属性,只有其中4个偶尔使用,那么为了性能问题,将表格分成2个表格是有意义的。

在这种情况下,把所有东西都放在一个表里不是很好。此外,处理有45列的表格也并不容易!


27
如果一个表中的数据与其他描述实体相关,但并不“属于”该实体,则应将其保持分离。

如果将来需要将这些单独的数据与其他实体相关联,这可能会带来优势。


16
使用这种方法最合理的时间是当有两个独立的概念,它们只能以这种方式相关联。例如,一辆汽车只能有一个当前司机,司机一次只能驾驶一辆车 - 因此,汽车和司机之间的关系将为1对1。我承认这是一种人为的例子,用于说明这一点。
另一个原因是你想以不同的方式专业化一个概念。如果你有一个人员表,并想添加不同类型的人员概念,如员工、客户、股东 - 每个人员概念都需要不同的数据集。它们之间相似的数据将在人员表上,而特定的客户、股东、员工表上则包含专业信息。
一些数据库引擎在给非常大的表(许多行)添加新列时很难有效地处理,我见过扩展表用于包含新列的情况,而不是将新列添加到原始表中。这是使用附加表的更为可疑的情况之一。
你也可以决定将单个概念的数据分成两个不同的表以解决性能或可读性问题,但如果你从头开始,这是一个相当特殊的情况 - 这些问题将在以后显现。

8

首先,我认为这是一个建模和定义什么构成独立实体的问题。假设您有一个只有一个单一地址的客户。当然,您可以在单个表customer中实现所有内容,但如果将来允许他拥有2个或更多地址,则需要重构该表(不是问题,但需做出明智决策)。

我还可以想到一个其他答案中没有提到的有趣情况,在这种情况下,拆分表可能会很有用:

再次想象,您有每个带有单个地址客户,但此时拥有地址是可选的。当然,您可以将其实现为一堆可为NULL 的列,例如ZIP,state,street。但是假设考虑到您确实拥有一个地址,则state是必需的,但ZIP是可选的。如何在单个表中进行建模?您可以在customer表上使用约束条件,但将其分割到另一个表中并使外键可以为NULL更容易。这样,您的模型更加明确地表明了实体address是可选的,并且ZIP是该实体的可选属性。


7

不太常见。

如果您需要实现一些安全性措施,例如让某些用户只能看到某些列(table1),而不能看到其他列(table2),那么可能会有所帮助。

当然,一些数据库(如Oracle)允许您在同一张表中进行此类安全性控制,但其他一些数据库则不行。


7
您正在讨论数据库规范化。 我维护的应用程序中可以想到一个示例是“项目”。该应用程序允许用户出售许多不同类型的物品(即InventoryItems,NonInventoryItems,ServiceItems等)。虽然我可以在一个项目表中存储每个项目所需的所有字段,但要保持可维护性,最好有一个基本项目表,其中包含所有项目共有的字段,然后为每种项目类型(即库存、非库存等)单独创建表格,这些表格包含仅适用于该项目类型的特定字段。 然后,项目表将具有指向它所代表的特定项目类型的外键。 特定项目表与基本项目表之间的关系将是一对一。
以下是有关规范化的文章。

http://support.microsoft.com/kb/283878


根据您的描述,似乎类型(例如InventoryItems)是Item的子类。难道您不会将外键放在子类型中吗?如果一个物品只能有一种类型,那么您的物品就需要为每种类型(InventoryItemId、NonInventoryItemId等)分配一个ID,其中一些ID可能为空。如果您在子项上有外键,那么您只需要一个(ItemId作为外键)。这可能允许一对多关系,其中您可以具有相同ID的多个子项。因此,如果是真正的1对1,您是否会在Item和子类型上使用相同的ID(PK)? - JohnDoe

5
另一个应用场景可能是这样的:您可能会从某些来源导入数据并每天更新,例如有关书籍的信息。然后,您会自己添加一些书籍数据。那么,将导入的数据放在另一个表中而不是您自己的数据表中就是有意义的。

5

在实践中,我通常遇到两种常见的1:1关系:

  1. IS-A关系,也称为超类型/子类型关系。这是指一种实体实际上是另一种实体的类型(EntityA IS A EntityB)。例如:

    • 人员实体,其中包括同一公司的会计师、工程师、销售人员等不同的实体。
    • 物品实体,其中包括小部件、原材料、成品等不同的实体。
    • 汽车实体,其中包括卡车、轿车等不同的实体。

    在所有这些情况下,超类型实体(例如人员、物品或汽车)将具有所有子类型共有的属性,而子类型实体将具有每个子类型各自独特的属性。子类型的主键与超类型的主键相同。

  2. “老板”关系。这是指一个人是组织单位(部门、公司等)的唯一老板或经理或主管。当组织单位只允许一个老板时,表示老板的人员实体和表示组织单位的实体之间存在1:1关系。


1
我喜欢第二个例子。你可以有实体“部门”和实体“员工”。在一个部门中,你可以有很多员工,而一个员工只能在一个部门工作。这是1:n的关系。一个员工可以是一个部门的主管 - 只能是一个部门,而且该部门只有一个主管。因此,你最终会得到两个表格,它们通过两个关系相互连接 - 1:n和1:1。 - cezar

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