在关系数据库中维护子类完整性

Mar*_*las 9 sql sql-server oop database-design

假设我有一张表代表超级班的学生.然后我有N个表格代表该对象的子类(运动员,音乐家等).我如何表达一个约束,以便学生必须在一个(不多于,不少于)子类中建模?

有关评论的澄清:

  • 这是手动维护,而不是通过ORM包.
  • 与此相关的项目位于SQL Server之上(但是看到通用解决方案会很高兴)
  • 这可能不是最好的例子.关于子类化,我们可以考虑几种情况,我碰巧发明了这个学生/运动员的例子.

A)以真正的面向对象的方式,超类可以自己存在,不需要在任何子类中建模.

B)在现实生活中,任何对象或学生都可以有多个角色.

C)我试图说明的特定场景要求每个对象只在一个子类中实现.将超类视为抽象实现,或者只考虑其他不同的对象类/实例中的共性.

感谢大家的投入,特别是比尔.

Bil*_*win 2

这里有几种可能性。一个是CHECK每个表中的一个,它student_id不会出现在任何其他姐妹子类型表中。这可能很昂贵,并且每次需要新的子类型时,都需要修改所有现有表中的约束。

CREATE TABLE athletes (
  student_id INT NOT NULL PRIMARY KEY,
  FOREIGN KEY (student_id) REFERENCES students(student_id),
  CHECK (student_id NOT IN (SELECT student_id FROM musicians 
                      UNION SELECT student_id FROM slackers 
                      UNION ...)) 
);
Run Code Online (Sandbox Code Playgroud)

编辑: @JackPDouglas 正确指出 Microsoft SQL Server 不支持上述形式的 CHECK 约束。事实上,根据 SQL-99 标准引用另一个表也是无效的(请参阅http://kb.askmonty.org/v/constraint_type-check-constraint)。

SQL-99 定义了多表约束的元数据对象。这称为ASSERTION,但是我不知道有任何 RDBMS 实现了断言。

可能更好的方法是使表中的主键students成为复合主键,第二列表示子类型。然后将每个子表中的该列限制为与该表表示的子类型相对应的单个值。 编辑:无需将 PK 作为子表中的复合键。

CREATE TABLE athletes (
  student_id INT NOT NULL PRIMARY KEY,
  student_type CHAR(4) NOT NULL CHECK (student_type = 'ATHL'),
  FOREIGN KEY (student_id, student_type) REFERENCES students(student_id, student_type)
);
Run Code Online (Sandbox Code Playgroud)

当然student_type也可以很容易地是一个整数,我只是将其显示为一个字符以用于说明目的。

如果您不支持CHECK约束(例如MySQL),那么您可以在触发器中执行类似的操作。

我阅读了您的后续内容,内容涉及确保超类表中的每一行都存在于某个子类表中。我认为没有一种实用的方法可以使用 SQL 元数据和约束来做到这一点。我建议满足此要求的唯一选择是使用Single-Table Inheritance。否则,您需要依赖应用程序代码来强制执行它。

编辑: JackPDouglas 还建议使用基于类表继承的设计。请参阅他的示例或我的类似技术示例(此处此处此处)

  • -1 表示有关检查约束的误导性信息。来自 [SQL Server 2008R2 文档](http://msdn.microsoft.com/en-us/library/ms174979(v=SQL.105).aspx):“搜索条件的计算结果必须为布尔表达式,并且不能引用另一张桌子。” (3认同)