4

我有实体关系模型(ERD),其中实体IndividualCategoryTeamCategory与实体Category有关。现在我想在Oracle DB中创建表。我一开始是这样的:Oracle中的外键约束

CREATE TABLE Category(
    category_id INT PRIMARY KEY, 
    ... 
); 

CREATE TABLE Individual_category(
    category_id INT CONSTRAINT fk_cat_indivcat REFERENCES Category(category_id), 
    ..., 
    CONSTRAINT pk_indivgamecat PRIMARY KEY (category_id) 
); 

CREATE TABLE Team_category(
    category_id INT CONSTRAINT fk_cat_teamcat REFERENCES Category(category_id), 
    ..., 
    CONSTRAINT pk_teamcat PRIMARY KEY (category_id) 
); 

外键主键约束,该组合保证了每一个Individual_category会有Category“超级”表中的相应记录(或“父”表?)。并且将只有一个IndividualCategory记录为特定的Category记录。 Team_category也一样。

要强制继承我需要多一个约束:即确保在Category每个记录都会有IndividualCategory(X)无论是记录或在TeamCategory记录但不能同时约束。

如何创建这样的约束?


编辑:这就是我说的 '于E-R模型的继承'。这是我的数据库老师的幻灯片(他们称之为“实体分型”的存在,但他们有时称之为它只是继承):enter image description here

+0

“继承”意味着分层数据,这个例子没有提示。只是外键...... – 2011-05-05 00:42:03

+0

@OMG小马:我不明白你的意思......确实在所有3个表格中有更多的列。并且'Category'中的所有列都应该被继承到两个子表。但怎么做呢?就是那个问题。 – drasto 2011-05-05 00:51:00

+1

您的术语会阻止您找到您所寻求的答案。没有“继承”,就像您对对象所使用的那样 - 外键仅验证列中的值是否已存在于约束所指的table.column中。 – 2011-05-05 00:54:46

回答

4

一个完全不同的方式来做到这一点使用延迟约束:

CREATE TABLE Category(
    category_id INT PRIMARY KEY, 
    team_category_id INT, 
    individual_category_id INT, 
    ... 
); 

CREATE TABLE Individual_category(
    individual_category_id INT PRIMARY KEY, 
    category_id INT NOT NULL, 
    ..., 
); 

CREATE TABLE Team_category(
    team_category_id INT PRIMARY KEY, 
    category_id INT NOT NULL, 
    ..., 
); 

确保一个Category是TeamCategory XOR的IndividualCategory:

alter table Category add constraint category_type_check check 
    ( (team_category_id is null and individual_category_id is not null) 
    or (team_category_id is not null and individual_category_id is null) 
); 

创建可延迟的完整性约束,使人们可以插入同一交易中的类别和团队/ Individual_Category;否则,您无法在TeamCategory/IndividualCategory之前插入类别,反之亦然。一个catch-22。

alter table category add constraint category_team_fk 
    foreign key (team_category_id) 
    references team_category (team_category_id) 
    deferrable initially deferred; 

alter table category add constraint category_individual_fk 
    foreign key (individual_category_id) 
    references individual_category (individual_category_id) 
    deferrable initially deferred; 

alter table individual_category add constraint individual_category_fk 
    foreign_key (category_id) 
    references category (category_id) 
    deferrable initially deferred; 

alter table team_category add constraint team_category_fk 
    foreign_key (category_id) 
    references category (category_id) 
    deferrable initially deferred; 
+0

这似乎是到目前为止我想做的最好的方法。 – drasto 2011-05-05 01:46:14

+1

这可能是教授想要的,现在定义类别的现实世界中的问题,您必须始终加入这些表,或者使用视图 - 先前的示例总是有的,而且此解决方案实际上更加复杂使用。 – 2011-05-05 01:59:23

+0

+1这是一个强大的尝试来解决一个“经典”的问题,虽然'类别'中的可为空的子类型键困扰我很多。问题当然是SQL(标准和实现)缺乏必要的'CREATE ASSERTION'和'CHECK'约束可以包含子查询,多重赋值和理想的“不相交”约束语法(所谓的“外部分布式密钥”等)。 – onedaywhen 2011-05-05 07:39:54

2

一个可以如何做到这一点,利用一个简单的例子:

CREATE TABLE Category(
    category_id INT PRIMARY KEY, 
    category_type varchar2(300) not null, 
    ... 
    [list of required attributes for only individual category, but nullable], 
    [list of required attributes for only team category, but nullable] 
); 

alter table category add constraint check_category_individual check 
    ( category_type <> 'INDIVIDUAL' 
    or ( category_type = 'INDIVIDUAL' 
     and [list of individual category attributes IS NOT NULL] 
    ) 
); 

alter table category add constraint check_category_team check 
    ( category_type <> 'TEAM' 
    or ( category_type = 'TEAM' 
     and [list of team category attributes IS NOT NULL] 
    ) 
); 

然后,您可以创建视图,如:

create view individual_category as 
select category_id, [base category attributes], [individual category attributes] 
    from category 
where category_type = 'INDIVIDUAL; 

你甚至可以把一个INSTEAD OF触发的视图,它会出现在应用程序能够像任何其他的表。

0

另一种在数据库中实现复杂约束的方法是使用物化视图(MV)。

在这个例子中的MV可以被定义如下:

create materialized view bad_category_mv 
refresh complete on commit 
as 
select c.category_id 
from category c 
left outer join individual_category i on i.category_id = c.category_id 
left outer join team_category i on t.category_id = c.category_id 
where ( (i.category_id is null and t.category_id is null) 
     or (i.category_id is not null and t.category_id is not null) 
    ); 

alter table bad_category_mv 
add constraint bad_category_mv_chk 
check (1=0) deferrable; 

所以MV是只为打破规则类别稀少,但随后的检查约束确保,结果在一个行中的任何交易MV将失败(因为1 = 0永远不会)。

我在过去的here上发表了关于这种方法的博客。

注意:尽管我对这种方法很感兴趣,但我从未在生产数据库中“愤怒”地使用它。需要进行仔细的基准测试,以确保每当数据发生变化时整个MV刷新的开销不会太高。

0

ERD继承是gen-spec设计模式的典型例子。关于如何在像Oracle这样的关系DBMS中设计gen-spec有很多文章。你可以通过谷歌搜索“泛化专业化关系建模”找到其中的一些。

您将在这些文章中看到的大部分内容已通过对此问题的其他回复概述。这个话题在SO中已经出现过很多次了。以前的讨论样本,click here

传统解决方案的主要特点是专用表具有一个id列,该id列是引用广义表的id列的主键和外键。以这种方式,这些子实体不会获得他们自己的身份。您真正需要注意的功能是实现析取的约束。并非所有文章都在他们提出的解决方案中强制执行这一规则。