很抱歉这个菜鸟问题,但是否真的需要与数据库中的表使用一对一的关系?您可以在一个表中实现所有必要的字段。即使数据变得非常大,您也可以在SELECT
语句中枚举您需要的列名,而不是使用SELECT *
. 你什么时候真的需要这种分离?
14 回答
1 到 0..1
超类和子类之间的“1 到 0..1”被用作实现继承的“所有类在单独的表中”策略的一部分。
“1 到 0..1”可以在单个表中表示,其中“0..1”部分由可空字段覆盖。但是,如果关系主要是“1 到 0”且只有少数“1 到 1”行,则将“0..1”部分拆分到单独的表中可能会节省一些存储(和缓存性能)优势。一些数据库在存储 NULL 方面比其他数据库更节俭,因此这种策略变得可行的“截止点”可能会有很大差异。
1比1
真正的“1 对 1”对数据进行垂直分区,这可能对缓存有影响。数据库通常在页面级别实现缓存,而不是在单个字段级别,因此即使您从一行中仅选择几个字段,通常也会缓存该行所属的整个页面。如果一行很宽而选定的字段相对较窄,您最终会缓存很多您实际上并不需要的信息。在这种情况下,对数据进行垂直分区可能很有用,因此只有更窄、更频繁使用的部分或行被缓存,因此更多的数据可以放入缓存中,从而使缓存有效地“更大”。
垂直分区的另一个用途是改变锁定行为:数据库通常不能在单个字段级别锁定,只能锁定整个行。通过拆分行,您允许锁定仅在其一半上发生。
触发器通常也是特定于表的。虽然理论上您可以只有一个表并让触发器忽略行的“错误的一半”,但某些数据库可能会对触发器可以做什么和不可以做什么施加额外的限制,这可能会使这不切实际。例如,Oracle 不允许您修改变异表 - 通过拥有单独的表,其中只有一个可能正在变异,因此您仍然可以从触发器修改另一个表。
单独的表可能允许更细粒度的安全性。
这些考虑在大多数情况下是无关紧要的,因此在大多数情况下,您应该考虑将“1 对 1”表合并为一个表。
另请参阅:为什么在数据库设计中使用一对一关系?
我的 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 ...)
如果您将两个一对一的表合二为一,您可能会遇到语义问题。例如,如果每个设备都有一个遥控器,那么将设备和遥控器及其一系列特性放在一个表中听起来不太好。您甚至可能不得不花时间确定某个属性是否属于设备或遥控器。
在某些情况下,您的一半列会长时间保持空白,或者永远不会被填满。例如,一辆汽车可能有一个拖车具有一堆特征,或者可能没有。所以你会有很多未使用的属性。
如果您的表有 20 个属性,并且偶尔使用其中的 4 个,那么出于性能问题将表分成 2 个表是有意义的。
在这种情况下,把所有东西都放在一张桌子上是不好的。此外,处理一个有 45 列的表并不容易!
如果一个表中的数据与另一个表描述的实体相关,但不“属于”另一个表,则可以将其分开。
如果单独的数据也需要与某个其他实体相关联,这在未来可能会提供优势。
如果有两个单独的概念只能以这种方式关联,那么使用它的最明智的时间是。例如,一辆车只能有一个当前的司机,司机一次只能驾驶一辆车——所以汽车和司机的概念之间的关系是 1 对 1。我接受这是一个人为的例子来演示观点。
另一个原因是你想以不同的方式专门化一个概念。如果您有一个 Person 表并且想要添加不同类型的 Person 的概念,例如 Employee、Customer、Shareholder - 其中每一个都需要不同的数据集。它们之间相似的数据将在 Person 表上,专家信息将在 Customer、Shareholder、Employee 的特定表上。
一些数据库引擎难以有效地将新列添加到一个非常大的表(许多行)中,我已经看到用于包含新列的扩展表,而不是将新列添加到原始表中。这是附加表更可疑的用途之一。
您可能还决定将单个概念的数据划分为两个不同的表,以解决性能或可读性问题,但如果您从头开始,这是一个相当特殊的情况 - 这些问题将在稍后显示。
不经常。
如果您需要实现一些安全性,您可能会发现一些好处 - 因此某些用户可以看到某些列(table1)但看不到其他列(table2)..
当然,有些数据库 (Oracle) 允许您在同一张表中执行这种安全性,但其他一些可能不允许。
您指的是数据库规范化。在我维护的应用程序中,我能想到的一个例子是 Items。该应用程序允许用户销售许多不同类型的项目(即 InventoryItems、NonInventoryItems、ServiceItems 等)。虽然我可以将每个项目所需的所有字段存储在一个 Items 表中,但维护一个包含所有项目共有字段的基本 Item 表,然后为每个项目类型(即 Inventory、NonInventory、等),其中包含特定于该项目类型的字段。然后,项目表将具有它所代表的特定项目类型的外键。特定项目表和基本项目表之间的关系将是一对一的。
下面是一篇关于规范化的文章。
首先,我认为这是一个建模和定义一个独立实体的问题。假设您拥有customers
一个且只有一个 single address
。当然,您可以在一个表中实现所有内容customer
,但是如果将来您允许他拥有 2 个或更多地址,那么您将需要对其进行重构(这不是问题,但要做出有意识的决定)。
我还可以想到一个在其他答案中没有提到的有趣案例,其中拆分表格可能很有用:
再次想象一下,您每个人都有customers
一个address
,但这次可以选择有一个地址。当然,您可以将其实现为一堆NULL
-able 列,例如ZIP,state,street
. 但是假设你确实有一个不是可选的,而是address
这个。如何在单个表中建模?您可以在表上使用约束,但在另一个表中划分并使foreign_key 可为空要容易得多。这样,您的模型更明确地说实体是可选的,这是该实体的可选属性。state
ZIP
customer
address
ZIP
与所有设计问题一样,答案是“视情况而定”。
有几点考虑:
表将有多大(在字段和行方面)?从维护和编程的角度来看,将用户名、密码与其他不太常用的数据一起存放可能很不方便
随着时间的推移,组合表中具有约束的字段可能会变得难以管理。例如,如果触发器需要针对特定字段触发,那么每次更新表都会发生这种情况,无论该字段是否受到影响。
你有多确定这种关系会是 1:1?正如这个问题所指出的,事情很快就会变得复杂。
我在实践中通常会遇到两种一般的 1:1 关系:
IS-A 关系,也称为超类型/子类型关系。这是当一种实体实际上是另一种实体的类型时(EntityA IS A EntityB)。例子:
- 个人实体,在同一公司内具有不同的会计、工程师、销售人员实体。
- Item 实体,具有 Widget、RawMaterial、FinishedGood 等单独的实体。
- 汽车实体,具有卡车、轿车等单独的实体。
在所有这些情况下,超类型实体(例如 Person、Item 或 Car)将具有所有子类型共有的属性,而子类型实体将具有每个子类型唯一的属性。子类型的主键与超类型的主键相同。
“老板”关系。这是当一个人是组织单位(部门、公司等)的唯一老板或经理或主管时。当一个组织单元只允许一个老板时,代表老板的个人实体和组织单元实体之间存在 1:1 的关系。
另一个用例如下:您可以从某个来源导入数据并每天更新它,例如关于书籍的信息。然后,您自己添加有关某些书籍的数据。然后将导入的数据放在另一个表中而不是您自己的数据中是有意义的。
另一个用例可能是超过了数据库表中的最大列数。然后你可以使用 OneToOne 加入另一个表
在我编程的时候,我只在一种情况下遇到过这种情况。即相同的 2 个实体(“实体 A”和“实体 B”)之间存在一对多和一对一的关系。
当“Entity A”有多个“Entity B”,“Entity B”只有1个“Entity A”,“Entity A”只有1个当前的“Entity B”,“Entity B”只有1个“Entity A”。
例如,一辆车只能有一个当前的司机,司机一次只能驾驶一辆车 - 所以汽车和司机的概念之间的关系是 1 对 1。 - 我从@Steve Fenton 的答案中借用了这个例子
驾驶员可以驾驶多辆汽车,但不能同时驾驶。所以 Car 和 Driver 实体是一对多或多对多的。但是如果我们需要知道当前的司机是谁,那么我们还需要一对一的关系。