我啥时候应该使用一对一的关系?

Posted

技术标签:

【中文标题】我啥时候应该使用一对一的关系?【英文标题】:When I should use one to one relationship?我什么时候应该使用一对一的关系? 【发布时间】:2012-09-01 09:10:45 【问题描述】:

很抱歉这个菜鸟问题,但是否真的需要对数据库中的表使用一对一关系?您可以在一个表中实现所有必要的字段。即使数据变得非常大,您也可以在SELECT 语句中枚举您需要的列名,而不是使用SELECT *。你什么时候真的需要这种分离?

【问题讨论】:

【参考方案1】:

1 到 0..1

超类和子类之间的“1 到 0..1”被用作 implementing inheritance 的“所有类在单独的表中”策略的一部分。

“1 到 0..1”可以在单个表中表示,其中“0..1”部分由可空字段覆盖。但是,如果关系大部分是“1 到 0”且只有少数“1 到 1”行,则将“0..1”部分拆分到单独的表中可能会节省一些存储空间(和缓存性能)的好处。一些数据库在存储 NULL 方面比其他数据库更节省,因此该策略变得可行的“截止点”可能会有很大差异。

1 到 1

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

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

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

单独的表可能允许更细粒度的安全性。

这些注意事项在大多数情况下是无关紧要的,因此在大多数情况下,您应该考虑将“1 对 1”表合并为一个表。

另见:Why use a 1-to-1 relationship in database design?

【讨论】:

【参考方案2】:

我的 2 美分。

我在一个我们都在开发大型应用程序的地方工作,一切都是一个模块。例如,我们有一个users 表,我们有一个为用户添加 facebook 详细信息的模块,另一个为用户添加 twitter 详细信息的模块。我们可以决定拔掉其中一个模块并从我们的应用程序中删除它的所有功能。在这种情况下,每个模块都将自己的表以 1:1 的关系添加到全局 users 表中,如下所示:

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

【讨论】:

【参考方案3】:

如果您将两个一对一的表放在一个表中,您可能会遇到语义问题。例如,如果每个设备都有一个遥控器,那么将设​​备和遥控器及其一系列特性放在一个表中听起来不太好。您甚至可能不得不花时间弄清楚某个属性是属于设备还是属于遥控器。

在某些情况下,您的一半列会长时间空着,或者永远不会被填满。例如,一辆汽车可能有一个拖车,有一堆特征,也可能没有。所以你会有很多未使用的属性。

如果您的表有 20 个属性,并且偶尔使用其中的 4 个,则出于性能问题将表分成 2 个表是有意义的。

在这种情况下,将所有东西都放在一张桌子上并不好。此外,处理一个有 45 列的表并不容易!

【讨论】:

【参考方案4】:

如果一个表中的数据与另一个表中描述的实体相关,但不“属于”另一个表中的实体,则可以将其分开。

如果单独的数据也需要与其他实体相关联,这可能会在未来提供优势。

【讨论】:

【参考方案5】:

如果有两个单独的概念只会以这种方式关联,那么使用它最明智的时机。例如,一辆车只能有一个当前的司机,司机一次只能驾驶一辆车——所以汽车和司机的概念之间的关系是 1 对 1。我接受这是一个人为的例子来演示点。

另一个原因是您想以不同的方式专门化一个概念。如果您有一个 Person 表并且想要添加不同类型的 Person 的概念,例如 Employee、Customer、Shareholder - 其中每一个都需要不同的数据集。它们之间相似的数据将在 Person 表中,专家信息将在 Customer、Shareholder、Employee 的特定表中。

一些数据库引擎难以有效地将新列添加到一个非常大的表(许多行)中,我已经看到用于包含新列的扩展表,而不是将新列添加到原始表中。这是附加表更可疑的用途之一。

出于性能或可读性问题,您还可以决定将单个概念的数据划分为两个不同的表,但如果您从头开始,这是一个相当特殊的情况 - 这些问题将在稍后出现。

【讨论】:

【参考方案6】:

不经常。

如果您需要实现一些安全性,您可能会发现一些好处 - 因此一些用户可以看到某些列 (table1) 而不是其他列 (table2)..

当然,有些数据库 (Oracle) 允许您在同一个表中执行这种安全性,但有些可能不允许。

【讨论】:

【参考方案7】:

您指的是数据库规范化。在我维护的应用程序中,我能想到的一个例子是 Items。该应用程序允许用户销售许多不同类型的项目(即 InventoryItems、NonInventoryItems、ServiceItems 等)。虽然我可以将每个项目所需的所有字段存储在一个 Items 表中,但维护一个包含所有项目共有字段的基本 Item 表,然后为每个项目类型(即 Inventory、NonInventory、等),其中包含特定于该项目类型的字段。然后,项目表将具有它所代表的特定项目类型的外键。特定项目表和基本项目表之间的关系将是一对一的。

下面是一篇关于规范化的文章。

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

【讨论】:

【参考方案8】:

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

我还可以想到一个在其他答案中没有提到的有趣案例,其中拆分表格可能很有用:

再次想象一下,您有一个 customers 和一个 address,但这次有一个地址是可选的。当然,您可以将其实现为一堆NULL-able 列,例如ZIP,state,street。但假设您确实有 address state 不是可选的,但 ZIP 是可选的。如何在单个表中建模?您可以在customer 表上使用约束,但在另一个表中划分并使foreign_key 可以为空要容易得多。这样,您的模型就更明确地说 entity address 是可选的,而 ZIP 是该实体的可选属性。

【讨论】:

【参考方案9】:

与所有设计问题一样,答案是“视情况而定”。

有几个注意事项:

表将有多大(在字段和行方面)?从维护和编程的角度来看,将用户名、密码与其他不太常用的数据一起存放可能很不方便

随着时间的推移,组合表中具有约束的字段可能会变得难以管理。例如,如果触发器需要针对特定​​字段触发,那么每次更新表都会发生这种情况,无论该字段是否受到影响。

您有多确定这种关系会是 1:1?正如This 问题所指出的,事情会很快变得复杂。

【讨论】:

【参考方案10】:

另一个用例如下:您可以从某个来源导入数据并每天更新它,例如有关书籍的信息。然后,您自己添加有关某些书籍的数据。然后将导入的数据放在另一个表中而不是您自己的数据中是有意义的。

【讨论】:

【参考方案11】:

我在实践中通常会遇到两种一般的1:1关系:

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

    个人实体,在同一公司内具有会计、工程师、销售人员的不同实体。 Item 实体,具有单独的 Widget、RawMaterial、FinishedGood 等实体。 汽车实体,具有卡车、轿车等单独的实体。

    在所有这些情况下,超类型实体(例如 Person、Item 或 Car)将具有所有子类型共有的属性,而子类型实体将具有每个子类型唯一的属性。子类型的主键与超类型的主键相同。

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

【讨论】:

我喜欢第二个例子。您可以拥有实体“部门”和实体“员工”。一个部门有很多员工,一个员工只能在一个部门工作。这是 1:n。一个员工可以是一个部门的主管——只有一个部门的主管,而这个部门只有一个主管。所以你最终会得到两个表,它们有两个关系 - 1:n 和 1:1。【参考方案12】:

在我编程的时候,我只在一种情况下遇到过这种情况。即相同的 2 个实体(“实体 A”和“实体 B”)之间存在一对多和一对一的关系。

当“Entity A”有多个“Entity B”而“Entity B”只有1个“Entity A”时 和 “实体 A”只有 1 个当前“实体 B”,“实体 B”只有 1 个“实体 A”。

例如,一个 Car 只能有一个当前的 Driver,而 Driver 一次只能驾驶一辆车 - 所以 Car 和 Driver 的概念之间的关系是 1 对 1。 - 我从 @ 借用了这个例子史蒂夫芬顿的回答

司机可以驾驶多辆汽车,但不能同时驾驶。所以 Car 和 Driver 实体是一对多或多对多的。但是如果我们需要知道当前的司机是谁,那么我们还需要一对一的关系。

【讨论】:

【参考方案13】:

另一个用例可能是超过了数据库表中的最大列数。然后你可以使用 OneToOne 加入另一个表

【讨论】:

【参考方案14】:

使用一对一关系的主要时间是在涉及继承时。

在下面,一个人可以是员工和/或客户。员工和客户继承人员属性。优点是如果一个人是员工和客户,他们的详细信息只存储一次,在通用人员表中。子表具有特定于员工和客户的详细信息。

【讨论】:

以上是关于我啥时候应该使用一对一的关系?的主要内容,如果未能解决你的问题,请参考以下文章

创建一对一关系的表

我啥时候应该在 JPA 中使用 @JoinColumn 或 @JoinTable?

Apriori算法的输出是不是应该是一对一的关系

Service 和 DAO 之间的关系应该是一对一还是一对多?

03-hibernate注解-关系映射级别注解

使用一对一关系实现一对多关系,没有循环依赖