超类和子类型在ER图中如何表示为表格?

18
我正在学习如何将实体关系图解释为SQL DDL语句,但是我对符号表示法的不同感到困惑。请考虑以下图示中的“不交”关系:

"Vehicle" box connects to "IsA" triangle, noted as "disjoint," which connects separately to "2WD" box and "4WD" box.

这应该表示为:

  1. Vehicle、2WD和4WD表(2WD和4WD将指向Vehicle的主键);或
  2. 仅有2WD和4WD表(没有Vehicle表),并且两者都会复制Vehicle可能具有的任何属性?

我认为这些是其他写法:

"Vehicle" box connects with a thick line to "IsA" triangle, which connects with thin lines separately to "2WD" box and "4WD" box. "Vehicle" box connects to "IsA" triangle, which connects separately to "2WD" box and "4WD" box, all by thin lines.

我希望能够清晰地解释每个图示所对应的最终表格的区别。


在关系型数据库术语中,这是一个简单、直接的「独占子类型」要求。UML不能用来定义RDbs,因为它没有IDEF1X的符号或丰富性。不幸的是,OO/ORM群体对RDbs和IDEF1X一无所知。「泛化-特化」无法充分涵盖它,没有任何模型或方法被给出。所有所需的约束都可以在SQL中实现,而无需进行「循环[重复]引用或延迟约束检查」、「超级键」或复制索引的疯狂操作。 - PerformanceDBA
4个回答

31

ER符号

有几种ER符号。我不熟悉您使用的那种,但很清楚你试图表示子类型(又称继承、类别、子类、泛化层次结构...)。这是面向对象编程中继承的关系型表达。

在进行子类型设计时,通常涉及以下决策:

  • 抽象 vs. 具体:父级是否可以实例化?在您的示例中:一个Vehicle能否存在而不需要同时是2WD4WD1
  • 包容性 vs. 排他性:是否可以为同一父级实例化多个子类?在您的示例中,Vehicle是否可以既是2WD又是4WD2
  • 完整 vs. 不完整:您是否希望将来添加更多的子类?在您的示例中,是否预计后续会将BikePlane(等)添加到数据库模型中?

信息工程符号区分包容性和排他性的子类型关系。另一方面,IDEF1X符号并没有(直接)认可这种差异,但它确实区分完整和不完整的子类型关系(IE则没有这样做)。

以下来自ERwin Methods Guide(第5章,子类型关系)的图表说明了差异:

enter image description here

IE和IDEF1X都不允许直接指定抽象或具体父级。

物理表示

不幸的是,实际数据库不直接支持继承,因此您需要将此图转换为实际表。通常有三种方法可以实现:

  1. 将所有类放入同一个表中,让子字段可以为空。然后您可以设置CHECK以确保非空字段的正确子集。
    • 优点:没有JOIN操作,因此某些查询可以受益。可以强制执行父级键(例如,如果您想避免具有相同ID的不同2WD和4WD车辆)。可以轻松地强制执行包含与排除孩子以及抽象与具体父级(通过只是改变 CHECK )。
    • 缺点:某些查询可能会比较慢,因为必须过滤出“无趣”的子项。根据您的DBMS,特定于子节点的约束可能会有问题。许多NULL可能会浪费存储空间。适用于不完整的子类型 - 添加新子节点需要更改现有表格,在生产环境中可能会有问题。
  2. 将所有子项放在单独的表中,但不要为父项创建表格(而是在所有子项中重复父项的字段和约束)。具有大部分3的特征,同时避免了JOIN,代价是可维护性较低(由于所有这些字段和约束重复)并且不能强制执行父级键或表示具体父级。
  3. 将父项和子项都放在单独的表中。
    • 优点:干净。不需要人为重复字段/约束。强制执行父级键并且易于添加特定于子项的约束。适用于不完整的子类型(相对容易添加更多的子表)。某些查询可以受益于仅查看“有趣”的子表。
    • 缺点:某些查询可能会涉及到JOIN操作。很难强制实施包含与排除孩子以及抽象与具体父级(如果DBMS支持循环和延迟的外键,则可以在声明性上强制实施这些内容,但通常认为在应用程序级别强制实施它们是一个次要的问题)。

正如您所看到的,情况并不理想-无论您选择哪种方法,都需要做出妥协。方法3可能应该是您的起点,只有在有充分的理由时才选择其中一种替代方案。


1 我猜测这是你们图表中所代表的线条粗细。

2 我猜测这是你们图表中“不相交”存在或不存在所代表的含义。


3
通常在数据库设计中,当您进行超类型/子类型关系时,需要为通用实体类型(超类型)创建一个单独的表,以及为您的专业实体版本(子类型)创建单独的表,无论是否不相交。在您的情况下,您需要创建一个车辆表,其中包含主键和一些所有子类型共享的属性。然后,您需要创建单独的2WD和4WD表,以及仅适用于这些表的特定属性。例如: 然后,您可以使用SQL连接查询这些表。

3
其他回答者所说的,再加上以下关于子类表的主键的内容。
你的情况看起来像是一种设计模式,被称为“泛化特殊化”,或简称为Gen-Spec。如何使用数据库表建模gen-spec的问题在SO中经常出现。
如果你正在使用Java等面向对象编程语言进行gen-spec建模,你将使用子类继承功能来处理细节。你只需定义一个类来处理通用对象,然后定义一组子类,每个专门对象类型对应一个子类。每个子类都继承通用类。这很容易和直接。
不幸的是,关系数据模型没有内置子类继承,SQL数据库系统也没有提供此类功能,据我所知。但你还没有倒霉。你可以设计你的表以在结构上与OOP的类结构相似地建模gen-spec。然后,当新项目添加到通用类时,您必须安排实现自己的继承机制。详情请见下文。
班级结构相当简单,有一个表用于通用类,每个特殊子类都有一个表。以下是Martin Fowler网站上的一个很好的说明Class Table Inheritance.。请注意,在此图中,Cricketer既是子类又是超类。您必须选择哪些属性放在哪个表中。该图显示了每个表中一个样本属性。
棘手的细节是如何为这些表定义主键。通用类表以通常的方式获得主键(除非此表是另一个泛化的专业化,例如板球手)。大多数设计师给主键一个标准名称,例如“Id”。他们使用自动编号功能来填充Id字段。特定类表获得一个主键,可以命名为“Id”,但不使用自动编号功能。相反,每个子类表的主键都受到约束,以引用广义表的主键。这使得每个专门的主键都成为外键和主键。请注意,在板球手的情况下,Id字段将引用Players中的Id字段,但Bowlers中的Id字段将引用Cricketers中的Id字段。
现在,当你添加新的项目时,你必须维护引用完整性。以下是具体方法。
首先,在gen表中插入一行新数据,并提供除主键之外的所有属性数据。自动编号机制将生成一个唯一的主键。接下来,在适当的spec表中插入一行新数据,包括所有属性数据,其中包括主键。你使用的主键是刚刚生成的全新主键的副本。这种主键的传播可以称为“穷人的继承”。
现在,当你想要从一个子类中获取所有广义数据和所有特定数据时,你只需要通过公共键连接这两个表。所有与所讨论的子类无关的数据都将从连接中删除。这很简单、快速、有效。

1

在实现特定数据模型时,并不总是只有一种方法。通常,当从逻辑模型转换为物理模型时会发生转换。

标准SQL没有干净的方法来强制执行不交子类型约束。

如果您的目标是使用模式尽可能多地强制执行模型的规则,则实现模型的标准方法是使用一个超类型表和每个子类型的一个表。这确保每个实体仅使用适用的属性。

有一种更或多或少标准的SQL技巧可用于强制执行不交约束。它让一些人感到不舒服,因为它以不重要的方式违反了规范化规则。尽管如此,一些人仍然认为这种技术在美学上是冒犯的,因为存在2NF的技术违规。

该技术涉及向超类型添加一个“分区属性”,并将此分区属性包含在每个子类型中,将其添加到子类型的主键中。除了强制执行分区属性的特定值的检查约束条件外,这还确保每个实体最多只能有一个子类型。该技术在许多地方都有详细记录,例如this blog

你在某个博客上读到了一些东西并且重复了它,这是很公平的,但真正的知识和经验是缺失的。技巧不是一个“标准”(只有标准组织制定标准)。它是一种可憎的行为,会导致重复的列和额外的索引。如果你喜欢大量复制和降低性能,那没问题,但强加给别人,并将其标记为“标准”,哎哎哎。这种可憎的行为显然是许多年前由乔·塞尔科发布的,作为他自己的观点。当我在c.d.t上与他讨论时,指出了它的荒谬之处。 - PerformanceDBA
鉴于可以在纯SQL“干净的方式”下完成而不需要额外的大规模复制,他放弃了并表示他没有发明它。违反规范化只有在不知道数据库设计的相关性和规范化价值时才是“不重要”的;这与美学无关。正确的方法(我不会建议它是“标准”的,我们已经知道它不为大众所知)是此答案。一个独占子类型通过服务器(而不是应用程序代码)声明实现并得到保证。没有重复;每个子类型没有额外的索引。 - PerformanceDBA
@PerformanceDBA 我不会与您就“标准”一词的潜在合法用途争论,因为这是DBA.SE而不是EL&U.SE。我要建议的是,在被付费开发业务应用程序的25年后,我认为我有足够的“真正的知识和经验”。至于“可憎之处”,我会说,与其极端地看待世界,我的经验告诉我,虽然经验法则很好,但最好理解它们背后的原理,并应用这些原理,而不是盲目遵循规则而不考虑其原因。 - Joel Brown

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