【问题标题】:Correct way to implement 1 to 0..1 relation in relational databases在关系数据库中实现 1 到 0..1 关系的正确方法
【发布时间】:2020-05-08 03:32:51
【问题描述】:

想象一下,在我们的数据模型中,我们有一个实体(一种数据结构),它具有可选部分。我们可以将这些“部分”实现为对其他(子)实体的可空引用。换句话说,主实体的每个实例可能具有或不具有与其关联的其他(子)实体的单个实例,并且子实体的任何实例仅具有与其关联的主实体的一个实例。所以我们有 1 到 0..1 的关系。

例如,审计日志记录有共同的字段,如(时间戳、用户、操作)以及特定于操作的部分(扩展信息),对于不同的操作可能完全不同。我们可以使用单独的实体来表示每种类型的扩展信息,然后让主实体对每种可能的扩展信息类型都有可空引用。

我可以看到在关系数据库中实现它的两种方式:

1。对主记录中子实体的可空引用

对于每种类型的子实体,主记录表都有一个字段引用子(扩展)表中记录的 ID 作为外键。

这似乎是更直接的选择:要检索相关信息,我们只需遵循直接引用即可。在 SQL 查询中,我们将通过外键左连接子(扩展)表。外键的空值将为我们提供所有子表字段的空值。

2。子实体的记录引用主实体的记录

我们不在主实体表中存储任何引用。相反,子实体表的每条记录都通过 ID 引用主表中的一条记录作为外键。在 SQL 查询中,我们仍然将子表左连接到主表,并且对于没有对应子记录的所有子表的字段,我们得到空值。

???

哪种方法是正确的?第二个似乎更relational,我们不必在主表中创建额外的字段,但从技术上讲,它可能需要更多的工作来查找相关记录,因为我们不是直接引用我们必须在子表中搜索主 ID。或者数据库引擎是否优化这种连接以使其快速,例如使用索引?索引搜索比扫描快,但仍然比直接引用慢。 Plus 索引占用空间。我填补了数据库引擎如何工作的知识不足......或者我只是错过了一些明显的东西。帮助将不胜感激。

更新

在得到下面的答案后,也有了更多的思考,决定使用第二种方法。除了在接受的答案中所说的(更紧凑,从关系的角度来看更正确,不必处理 NULL),如果我需要删除所有对应的主记录,它还为我提供了使用级联删除的很好的可能性子记录。

【问题讨论】:

  • 除非您定义它,否则工程中没有“更好”/“最佳”之类的东西。同样不幸的是,所有合理的实际定义都需要大量的经验,以及与对细节的混乱敏感度相互作用的大量因素。进行简单的设计。当您通过测量证明您可以想到的设计和所有替代方案都存在问题时(无论当时意味着什么),然后提出一个非常具体的问题。这也应该定义“更好”/“最好”。 Strategy for “Which is better” questions
  • @philipxy 感谢您的评论,尽管它没有帮助。我知道这可能看起来像是一个基于意见的问题,但实际上并非如此。我相信这样做有正确和不正确的方法,我只是不知道,这是正确的。我已编辑问题以删除“最佳实践”触发词。
  • 您的帖子没有提出可回答的问题,因为它没有提供足够的细节或选择标准。你没有不知道答案的问题。您只是在一些观察中感到疑惑。
  • Philip 的 cmets 不正确,您可以忽略它们。你是对的,你用两个例子给出了一个问题。有很多方法可以解决或部分解决这个单一问题……但只有一种关系(科学上正确)的方法。然而问题是,不了解科学的人会认为这是一个“基于意见”的问题。比如菲利普的评论,以及结束问题的投票。
  • 观察接受的答案帖子没有回答您提出的问题并表示无法回答,并重复我的评论“最佳”。

标签: database-design relational-database database-normalization


【解决方案1】:

在这种情况下,我认为“最佳”解决方案取决于数据库的工作负载,因为这两种解决方案各有优缺点。

第二种解决方案没有空值,这通常会使查询复杂化(对于关系模型的纯粹主义者来说,空值也使解决方案不“正确”),但还有其他好处:它需要更少的空间(所以对于某些类型的操作,关系更紧凑并且需要更少的时间来操作)。另一方面,它需要连接才能访问详细数据(因此这些操作需要额外的索引)。

第一个解决方案在概念上更简单,在访问详细数据时不需要连接,但需要更多空间,这会减慢某些类型的操作。

这两种解决方案都在实际环境中使用。

我认为,只有知道必须在此类数据库上部署的应用程序的典型工作负载是什么,才能提供解决此困境的方法:某些查询相对于其他查询是否更频繁(或必须具有更少的延迟)?例如,只查看一般数据的查询比需要详细数据的查询更常用?

最后,如果很难或不可能进行这种“理论”分析,唯一的另一种方法是尝试一种解决方案,但如果性能不令人满意,则准备尝试另一种解决方案。这可以通过使用视图来完成,例如以这种方式:

  1. 从第二个解决方案开始,定义一个执行连接的视图。

  2. 在适当的时候使用基表或连接视图来编写应用程序。

  3. 如果性能不令人满意,切换到另一个解决方案,通过连接创建一个与旧视图同名的新表,并定义一个仅对非空属性执行投影的新视图.

通过在这两种情况下为视图和基表交换相同的名称,应用程序将只需要进行最少的修改,您可以对这两种方法进行试验。

【讨论】:

    猜你喜欢
    • 2011-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多