Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 在关系数据库中维护子类完整性_Sql_Sql Server_Database Design_Oop - Fatal编程技术网

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

Sql 在关系数据库中维护子类完整性,sql,sql-server,database-design,oop,Sql,Sql Server,Database Design,Oop,假设我有一个代表超级班级的表,学生。然后我有N个表来表示该对象的子类(运动员,音乐家,等等)。我如何表达一个约束,使一个学生必须在一个(不是更多,不是更少)子类中建模 关于评论的澄清: 这是手动维护的,而不是通过ORM包 与此相关的项目位于SQL Server之上(但最好能看到通用解决方案) 这可能不是最好的例子。关于子类,我们可以考虑几个场景,我正好发明了这个学生/运动员的例子。 A) 在真正的面向对象方式中,超类可能独立存在,并且不需要在任何子类中建模 B) 在现实生活中,任何对象或学生

假设我有一个代表超级班级的表,学生。然后我有N个表来表示该对象的子类(运动员音乐家,等等)。我如何表达一个约束,使一个学生必须在一个(不是更多,不是更少)子类中建模

关于评论的澄清:

  • 这是手动维护的,而不是通过ORM包
  • 与此相关的项目位于SQL Server之上(但最好能看到通用解决方案)
  • 这可能不是最好的例子。关于子类,我们可以考虑几个场景,我正好发明了这个学生/运动员的例子。
A) 在真正的面向对象方式中,超类可能独立存在,并且不需要在任何子类中建模

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

C) 我试图说明的特定场景要求每个对象只在一个子类中实现。可以将超类看作是一个抽象实现,或者仅仅是从其他完全不同的对象类/实例中分离出来的共性


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

这里有几个可能性。一个是在每个表中进行
检查
,以确保
学生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 ...)) 
);
编辑:@JackPDouglas正确地指出,Microsoft SQL Server不支持上述形式的检查约束。事实上,根据SQL-99标准,引用另一个表也是无效的(请参阅)

SQL-99为多表约束定义元数据对象。这称为断言,但是我不知道任何实现断言的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)
);
当然,
student\u type
也可以很容易地成为一个整数,我只是将其显示为一个字符,以便于说明

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

我读了你的后续文章,关于确保超类表中的每一行在某个子类表中都存在一行。我不认为有一种实用的方法可以通过SQL元数据和约束实现这一点。我能建议满足此要求的唯一选项是使用。否则,您需要依赖应用程序代码来实施它


编辑:JackPDouglas还建议使用基于的设计。请参阅或我的类似技术或问题示例。

有趣的问题。当然,子表有FK约束,因此必须有一个学生

主要问题是在插入时尝试进行检查。必须首先插入student,这样才能避免违反子表中的FK约束,从而使执行检查的触发器无法工作

如果你真的关心这个问题,你可以编写一个应用程序,不时检查一下。我认为最大的恐惧是删除。有人可以删除子表条目,但不能删除学生。您可以使用触发器来检查何时从子表中删除项,因为这可能是最大的问题


我有一个db,每个子类层次结构都有一个表,就像这样。我正确地使用Hibernate及其映射,因此它会自动删除所有内容。如果是“手工”完成的话,我会确保总是用适当的级联删除父对象(呵呵:)

谢谢,比尔。你让我想

超类表有一个子类代码列。每个子类表都有一个外键约束,以及一个指示id与超类表的一个子集(其中code=atternate)一起存在的约束

这里唯一缺少的部分是,可以在没有子类的情况下对超类进行建模。即使将“代码”列设为必填项,它也可能只是一个空联接。这可以通过添加一个约束来解决,即超类的ID存在于子类表中ID的并集中。如果在事务的中间执行约束,则插入会有这两个约束。或者不用担心未分类的对象

编辑:哎呀,听起来真是个好主意。。。但由于不支持引用其他表的子查询,这一事实阻碍了这一进程。至少不是在SQL Server中

这可以通过添加一个约束来解决,该约束使超类的ID存在于 子类表中的ID


根据您希望在模式中输入多少智能(以及MS SQL Server允许您输入多少智能),您实际上不需要对子类表进行并集,因为您知道,如果id存在于任何子类表中,它必须与子类代码列标识的子类位于同一个子类中。

每个学生记录都有一个子类列(为便于论证,假设它是一个字符(1))。{A=运动员,M=音乐家…}

现在创建您的运动员和音乐家表。它们还应该有一个子类列,但是应该有一个检查约束,对它们所表示的表类型的值进行硬编码。例如,您应该为运动员表上的子类列设置默认值“a”和检查约束“a”

使用StudentID和子类的复合外键将音乐家和运动员表链接到Student表。你完了!去喝一杯可口的co
CREATE TABLE Student (
    StudentID INT NOT NULL IDENTITY PRIMARY KEY,
    SubClass CHAR(1) NOT NULL,
    Name VARCHAR(200) NOT NULL,
    CONSTRAINT UQ_Student UNIQUE (StudentID, SubClass)
);

CREATE TABLE Athlete (
    StudentID INT NOT NULL PRIMARY KEY,
    SubClass CHAR(1) NOT NULL,
    Sport VARCHAR(200) NOT NULL,
    CONSTRAINT CHK_Jock CHECK (SubClass = 'A'),
    CONSTRAINT FK_Student_Athlete FOREIGN KEY (StudentID, Subclass) REFERENCES Student(StudentID, Subclass)
);

CREATE TABLE Musician (
    StudentID INT NOT NULL PRIMARY KEY,
    SubClass CHAR(1) NOT NULL,
    Instrument VARCHAR(200) NOT NULL,
    CONSTRAINT CHK_Band_Nerd CHECK (SubClass = 'M'),
    CONSTRAINT FK_Student_Musician FOREIGN KEY (StudentID, Subclass) REFERENCES Student(StudentID, Subclass)
);