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列的主鍵和外鍵。以這種方式,這些子實體不會獲得他們自己的身份。您真正需要注意的功能是實現析取的約束。並非所有文章都在他們提出的解決方案中強制執行這一規則。