ER图中的超级和子types关系如何表示?
我正在学习如何将实体关系图解释为SQL DDL语句,我对表示法的差异感到困惑。 考虑一个不相交的关系,如下图所示:
这可以表示为:
- 车辆,2WD和4WD表(2WD和4WD将指向车辆的PK); 要么
- 只有2WD和4WD表(和NO车辆表),这两个表将会复制车辆将具有的属性?
我认为这是写作关系的其他方式:
我正在寻找一个清楚的解释,关于你最终将为每个图表的差异。
ER表示法
有几个ER符号。 我不熟悉你正在使用的那个,但是很明显,你正试图表示一个子types(又名inheritance,类别,子类,泛化层次等)。 这是OOPinheritance的关系表亲。
在进行分类时,您通常会关注以下devise决策:
- 抽象与具体:父母是否可以被实例化? 在你的例子中:一辆
Vehicle
存在, 而不是2WD
或4WD
? 1 - 包容性与独占性:一个以上的孩子可以为同一父母实例化吗? 在你的例子中,
Vehicle
可以是2WD
和4WD
吗? 2 - 完整还是不完整:您是否预计未来会增加更多的孩子? 在你的例子中,你期望一个
Bike
或一个Plane
(等等)可以稍后添加到数据库模型?
信息工程符号区分包容性和排他性的子types关系。 另一方面,IDEF1X表示法不能(直接)认识到这种差异,但它区分完整和不完整的子types(哪个IE没有)。
ERwin方法指南 (第5章,子types关系)的下图说明了不同之处:
IE和IDEF1X都不允许指定抽象与具体的父级。
物理表示
不幸的是,实际的数据库不能直接支持inheritance,所以你需要把这个图转换成真正的表。 通常有三种方法可以做到这一点:
- 把所有的类放在同一个表中,并将子字段留空。 然后您可以有一个CHECK来确保非NULL的字段的正确的子集。
- 优点:没有join,所以一些查询可以受益。 可以执行父级密钥(例如,如果您想避免具有相同ID的不同的
2WD
和4WD
车辆)。 可以很容易地执行包容性与专有的孩子和抽象与具体的父母(通过只是改变检查)。 - 缺点:一些查询可能会比较慢,因为他们必须过滤掉“无趣”的孩子。 根据您的数据库pipe理系统,特定于儿童的约束条件可能会有问题。 大量的NULL会浪费存储空间。 不太适合不完整的子types – 添加新的子级需要更改现有的表,这在生产环境中可能会有问题。
- 优点:没有join,所以一些查询可以受益。 可以执行父级密钥(例如,如果您想避免具有相同ID的不同的
- 把所有的孩子放在不同的表格中,但没有父母的表格(而是在所有的孩子中重复父母的字段和约束条件)。 具有(3)的大部分特点,同时避免JOIN,代价是可维护性较低(由于所有这些领域和约束重复),无法强制父级密钥或代表一个具体的父母。
- 把父母和孩子放在不同的表格中。
- 优点:清洁。 任何领域/限制都不需要人为重复。 实施父级密钥并轻松添加特定于子级的约束。 适用于不完整的子types(相对容易添加更多的子表)。 某些查询可以通过只查看“有趣”的子表获益。
- 缺点:一些查询可能会加重连接。 如果DBMS支持循环和延迟外键,但是在应用程序级别执行它们通常被认为是一个较小的错误,那么可以很难强制执行包容性与独占子级和抽象与具体父级(这些可以被声明性强制执行)。
正如你所看到的,情况并不理想 – 你需要以任何方式妥协。 方法(3)可能应该是你的出发点,如果有强制性的理由,只能select其中一种。
1我猜这是线条的粗细代表你的图表。
2我猜这是图表中存在或不存在“不相交”的地方。
还有其他的响应者说了什么,再加上下面的子类表的主键。
您的案例看起来像devise模式的一个实例,被称为“泛化专业化”(Gen-Spec)。 如何使用数据库表来build模gen-spec的问题始终在SO中出现。
如果您在OOPL(如Java)中对gen-spec进行build模,则可以使用子类inheritance工具来为您处理细节。 您只需定义一个类来处理泛化对象,然后定义一个子类集合,每个types的专用对象都有一个子集合。 每个子类都会扩展广义类。 这很简单,直接。
不幸的是,关系数据模型没有内置子类inheritance,SQL数据库系统不提供任何这样的设施,据我所知。 但是,你不是运气不好。 您可以devise您的表格,以与OOP的类结构相似的方式对gen-spec进行build模。 当新项目添加到广义类时,您必须安排实现您自己的inheritance机制。 详情如下。
类结构相当简单,一个表为gen类,一个表为每个spec子类。 这是Martin Fowler网站的一个很好的例子。 类表inheritance。 请注意,在此图中,Cricketer既是一个子类又是一个超类。 你必须select哪些属性放在哪个表中。 该图显示了每个表中的一个示例属性。
棘手的细节是如何定义这些表的主键。 gen类表以通常的方式得到一个主键(除非这个表是另一个通用化的特殊化元素,比如Cricketers)。 大多数devise师给主键一个标准名称,如“Id”。 他们使用自动编号function来填充Id字段。 spec类表获得一个主键,可以命名为“Id”,但不使用自动编号function。 相反,每个子类表的主键被约束为引用广义表的主键。 这使得每个专用的主键都是一个外键以及一个主键。 请注意,对于板球运动员来说,Id域将引用Players中的Id域,但Bowlers中的Id域将引用板球运动员中的Id域。
现在,当你添加新的项目,你必须保持参照完整性,这是如何。
您首先在生成表中插入一个新行,为除主键以外的所有属性提供数据。 自动编号机制生成一个唯一的主键。 接下来,在适当的规格表中插入新行,包括其所有属性的数据,包括主键。 您使用的主键是刚刚生成的全新主键的副本。 主键的传播可以被称为“穷人的inheritance”。
现在当你想把所有的泛化数据和来自一个子类的所有专用数据放在一起的时候,你所要做的就是通过共同的键来join这两个表。 所有不属于有问题的子类的数据将退出连接。 这是光滑,简单,快速。
通常,在数据库devise中执行超types/子types关系时,需要为通用实体types(超types)和专用实体版本(子types)的单独表格创build单独的表格,不连贯或不连贯。 在你的情况下,你将需要为VEHICLE和一个主键以及所有子types共有的或共享的一些属性创build一个表。 然后,您将需要为2WD和4WD创build单独的表以及仅针对这些表的属性。 例如
那么你可以通过使用SQL连接来查询这些表
实现任何特定的数据模型并不总是只有一种方法。 通常情况下,从逻辑模型转换到物理模型时会发生转换。
标准的SQL没有一个干净的方法来强制不相交的子types约束。
如果您的目标是使用模式尽可能多地执行您的模型的规则,那么实现您的模型的标准方法是使用超types的表格和每个子types的表格。 这确保了每个实体仅使用适用的属性。
实施不相交约束有一个或多或less标准的SQL技巧。 它把一些人放弃了,因为它违反了正常化规则,并不重要。 尽pipe如此,有些人还是觉得这个技术在美观上是有攻击性的,因为有技术上的违反了2NF。
这种技术涉及向超types添加分区属性 ,并在每个子types中包含此分区属性,并将其添加到子types的主键。 除了为分区属性强加特定值的检查约束之外,这可确保每个实体最多只能有一个子types。 该技术在很多地方都有详细logging,比如这个博客 。