超类型-子类型数据库设计
超类型-子类型数据库设计
我对关系数据库中的超类型-子类型设计有一个问题。如果我有一个超类型和两个子类型表,我会把超类型的PK与两个子类型表的PK相关联作为外键。假设我有类似这样的东西:
Type
TypeID PK
SuperType
ID PK
TypeID FK
SubtypeA
ID PK,FK
SubtypeB
ID PK,FK
在数据库层面上,我该如何确保给定类型的超类型ID只能放入适当的子类型表中?例如,我不希望将类型A的超类型ID放入SubtypeB表中。有没有一种简单的方法在数据库层面上防止这种情况发生?我知道这可以在代码中处理,但如果代码有错误呢?或者如果有人手动输入错误的ID到其中一个子类型表中呢?我想知道有没有办法在数据库层面上防止这种情况发生。
有什么想法吗?也许超类型表上的PK应该是ID和TypeID的组合,并在ID列上设置唯一约束,以防止在超类型表中出现同时具有两种类型的记录...然后子类型表将具有组合ID和TypeID的主键,并在TypeID上设置约束,只允许适当子类型表的类型出现?
超类型-子类型数据库设计问题的原因是如何确保给定类型的超类型ID仅放入适当的子类型表中。为了解决这个问题,可以使用以下方法:
方法一:
在支持延迟约束的数据库管理系统上,可以使用以下约束条件:
CHECK ( ( (SubtypeAId IS NOT NULL AND SubtypeAId = SuperTypeId) AND SubtypeBId IS NULL ) OR ( SubtypeAId IS NULL AND (SubtypeBId IS NOT NULL AND SubtypeBId = SuperTypeId) ) )
通过使用这些特殊的循环外键(FK),结合CHECK约束条件,可以确保子类型的互斥性和存在性。当插入新数据时,将子类型的外键(或CHECK约束条件,如果DBMS支持)延迟执行,以打破鸡生蛋的问题。
方法二:
另一种方法是只强制互斥性,而不强制存在性,可以使用以下约束条件:
CHECK(TypeId = 1)
对于SubtypeA表,和以下约束条件:
CHECK(TypeId = 2)
对于SubtypeB表。通过这种方法可以确保子类型的互斥性。如果数据库管理系统不支持“非键”外键,可能需要在SuperType表上添加冗余的UNIQUE约束(SuperType {SuperTypeId, TypeId})。
另外,可以通过在子类型的TypeId上使用计算列(例如Oracle 11的虚拟列)来节省存储空间。
另一种解决方法是通过应用程序逻辑强制存在性和互斥性,尽管通常应该尽量在数据库中实施尽可能多的完整性约束,但在这种特殊情况下,通过应用程序级别进行约束往往是合理的,以避免上述复杂性。
最后,"all classes in separate tables"并不是实现继承的唯一策略。如果使用"everything in one table"或者"concrete classes in separate tables"来实现继承,将更容易强制子类型的存在性和互斥性。更多信息可以参考上述链接。
在数据库设计中,有时候会遇到Supertype-subtype的情况,即一个父类表(supertype table)和多个子类表(subtype table)的关系。这种设计的出现是为了解决一些实体之间的继承关系或者多态性的需求。
然而,在这种设计中,一个常见的问题是如何将新的父类表的记录自动传播到相应的子类表中。解决这个问题的方法是使用触发器(trigger)。
触发器是一种特殊的数据库对象,可以在数据库中的某个事件发生时自动执行一些操作。在这种情况下,我们可以创建一个触发器,使得当在父类表中插入新记录时,自动将该记录传播到子类表中。
以下是一个使用触发器的示例(假设父类表名为supertype,子类表名为subtype):
CREATE TRIGGER propagate_entry
AFTER INSERT ON supertype
FOR EACH ROW
BEGIN
INSERT INTO subtype (col1, col2, col3)
VALUES (NEW.col1, NEW.col2, NEW.col3);
END;
在上述示例中,我们创建了一个名为propagate_entry的触发器。当在supertype表中插入新记录时,触发器会自动执行INSERT语句将该记录插入到subtype表中。这里的NEW关键字表示插入操作中的新记录。
通过使用触发器,我们可以解决Supertype-subtype数据库设计中的新记录传播问题。这样一来,无论是在父类表还是子类表中进行操作,都能保持数据的一致性和完整性。