2012-01-11 71 views
2

我正在研究Oracle中的一組數據庫表,並試圖找出一種方法來強制實施具有輕微多態數據的參照完整性。Oracle - 使用多種類型的數據進行參照完整性

具體來說,我有一堆不同的表 - 假設,假設我有蘋果,香蕉,橙子,橘子,葡萄,一百多個種類的水果。現在我正在嘗試製作一張描述執行涉及水果步驟的表格。所以我想插入一行說「吃蘋果ID 100」,然後是另一行說「剝香蕉ID 250」,然後另一行說「冷藏橘ID 500」,等等。

從歷史上看,我們已經在兩個方面做到了這一點:

1 - 包括針對每種可能的水果列。使用檢查約束來確保除一列之外的所有列都爲NULL。使用外鍵來確保我們的水果的參照完整性。所以在我的假設的例子中,我們有一個表格,其中列ACTION, APPLEID, BANANAID, ORANGEID, TANGERINEIDGRAPEID。對於第一個動作,我們會有一排'Eat', 100, NULL, NULL, NULL, NULL, NULL。對於第二個動作,我們有'Peel', NULL, 250, NULL, NULL, NULL。等等,等等

這種方法非常適合自動獲得所有的Oracle的RI的好處,但它只是不擴展到一百種水果。你最終會得到太多的欄目以實用。只要確定你正在處理哪種水果就成了一項挑戰。

2 - 包括與水果的名稱的列,並用水果ID的列。這也適用,但是沒有任何方法可以讓Oracle以任何方式強制執行數據的有效性。所以我們的專欄將是ACTION, FRUITTYPEFRUITID。行數據將是'Eat', 'Apple', 100,然後'Peel', 'Banana', 250等等。但是沒有任何東西阻止某人刪除Apple ID 100,或者插入一個步驟說'Eat', 'Apple', 90000000即使我們沒有具有該ID的Apple。

有沒有辦法避免維護每一單果型單獨的列,但仍保留外鍵的大部分好處? (或者在技術上,我可以確信使用百列,如果我可以隱藏有絕招莫名其妙的複雜性它只是看起來理智在每天的日常使用。)

澄清:在我們實際的邏輯,「果實」是完全不同的表,很少有共同性。考慮客戶,員工,會議室,房間,建築物,資產標籤等。步驟列表應該是自由形式的,並允許用戶在這些事情上指定行動。如果我們有一張包含這些不相關事物的表格,我不會有任何問題,但它也會是一個非常奇怪的設計。

+0

所以意圖是有一個「食譜」表與一組食譜行動?如「剁碎的奶奶 - 蘋果」,「切片c ban香蕉」,「把奶奶蘋果放在碗裏」,「把草莓香蕉放在碗裏」等等。據推測,這些作品只能在蘋果上切割,而切片只能用在香蕉上,但是也可以放在碗裏,而一些機器人會使用這個配方表來了解如何製作水果沙拉? (抱歉評論,因爲某些原因無法編輯原件) – Brian 2012-01-11 19:58:45

+0

@Brian:您可以編輯評論長達5分鐘。但是你仍然可以刪除第一條評論。 – 2012-01-11 21:48:09

+0

@Brian:這是一個很好的方式來描述我問的問題。實際上,一個機器人會檢查香蕉是否被切成薄片,蘋果是否被切碎,葡萄放在碗裏。一旦它認識到所有這些事情都已經發生,它就會正確地確定我們有完成的水果沙拉。 – StilesCrisis 2012-01-11 23:52:37

回答

6

目前尚不清楚對我,爲什麼你需要確定任務表的FRUIT_TYPE。表面上看,這只是一個糟糕的(非標準化)數據模型。根據我的經驗,對這類數據進行建模的最佳方式是使用超類型的通用事物(例如FRUIT)和細節類型(APPLE,GRAPE,BANANA)的子類型。這允許我們在記錄每個實例的特定屬性的同時將常見屬性存儲在一個地方。

這裏是超類型的表:

create table fruits 
    (fruit_id number not null 
     , fruit_type varchar2(10) not null 
     , constraint fruit_pk primary key (fruit_id) 
     , constraint fruit_uk unique (fruit_id, fruit_type) 
     , constraint fruit_ck check (fruit_type in ('GRAPE', 'APPLE', 'BANANA')) 
    ) 
/

水果具有主鍵和化合物唯一密鑰。我們需要在外鍵約束中使用主鍵,因爲複合鍵是一個痛苦的脖子。除非它們不是這些子類型表的情況。這裏我們使用的唯一密鑰作爲參考,因爲通過限制在子類型FRUIT_TYPE的價值,我們可以保證的是,在GRAPES表映射記錄類型「葡萄」的水果記錄等

create table grapes 
    (fruit_id number not null 
     , fruit_type varchar2(10) not null default 'GRAPE' 
     , seedless_yn not null char(1) default 'Y' 
     , colour varchar2(5) not null 
     , constraint grape_pk primary key (fruit_id) 
     , constraint grape_ck check (fruit_type = 'GRAPE') 
     , constraint grape_fruit_fk foreign key (fruit_id, fruit_type) 
       references fruit (fruit_id, fruit_type) 
     , constraint grape_flg_ck check (seedless_yn in ('Y', 'N')) 
    ) 
/

create table apples 
    (fruit_id number not null 
     , fruit_type varchar2(10) not null 
     , apple_type varchar2(10) not null default 'APPLE' 
     , constraint apple_pk primary key (fruit_id) 
     , constraint apple_ck check (fruit_type = 'APPLE') 
     , constraint apple_fruit_fk foreign key (fruit_id, fruit_type) 
       references fruit (fruit_id, fruit_type) 
     , constraint apple_type_ck check (apple_type in ('EATING', 'COOKING', 'CIDER')) 
    ) 
/

create table bananas 
    (fruit_id number not null 
     , fruit_type varchar2(10) not null default 'BANANA' 
     , constraint banana_pk primary key (fruit_id) 
     , constraint banana_ck check (fruit_type = 'BANANA') 
     , constraint banana_fruit_fk foreign key (fruit_id, fruit_type) 
       references fruit (fruit_id, fruit_type) 
    ) 
/

在11g中,我們可以使FRUIT_TYPE成爲子類型的虛擬列,並取消檢查約束。所以,現在我們需要一個任務類型表('Peel','Refrigerate','Eat'等)。

create table task_types 
    (task_code varchar2(4) not null 
    , task_descr varchar2(40) not null 
    , constraint task_type_pk primary key (task_code) 
    ) 
/

而實際的TASKS表是FRUITS和TASK_TYPES之間的簡單交集。

create table tasks 
    (task_code varchar2(4) not null 
    , fruit_id number not null 
    , constraint task_pk primary key (task_code, fruit_id) 
    , constraint task_task_fk ask foreign key (task_code) 
      references task_types (task_code) 
    , constraint task_fruit_fk foreign key (fruit_id) 
      references fruit (fruit_id) 
/

如果不能滿足您的需求,請編輯您的問題,包括更多的信息。


「......如果你想爲不同的水果不同的任務......」

是的,我不知道這是否是OP公司發佈的設計背後的動機。但通常情況下,工作流程比這要困難得多:有些任務將適用於所有水果,有些僅適用於(例如)成串的水果,其他的只適用於香蕉。


「在我們實際的邏輯,‘水果’與 很少共性完全不同的表。想想客戶,員工,會議室, 建築,資產標籤等步驟的列表應該是 自由形式,並允許用戶指定這些事情的任何行爲。「

所以你有一堆現有的表。您希望能夠將這些表中的記錄以自由滑動的方式分配給任務,但能夠保證擁有該任務的特定記錄的識別。

我認爲你仍然需要一個通用表來保存任務中的actor的ID,但是你需要以某種方式將它鏈接到其他表。下面是我可能接近它:

SOEM樣品現有的表:

create table customers 
    (cust_id number not null 
     , cname varchar2(100) not null 
     , constraint cust_pk primary key (fruit_id) 
    ) 
/

create table employees 
    (emp_no number not null 
     , ename varchar2(30) not null 
     , constraint emp_pk primary key (fruit_id) 
    ) 
/

一個普通的表來保存演員:

create table actors 
    (actor_id number not null 
     , constraint actor_pk primary key (actor_id) 
    ) 
/

現在,你需要交集表將現有的表有關聯新的:

create table cust_actors 
    (cust_id number not null 
     , actor_id number not null 
     , constraint cust_actor_pk primary key (cust_id, actor_id) 
     , constraint cust_actor_cust_fk foreign key (cust_id) 
       references customers (cust_id) 
     , constraint cust_actor_actor_fk foreign key (actor_id) 
       references actors (actor_id) 
    ) 
/

create table emp_actors 
    (emp_no number not null 
     , actor_id number not null 
     , constraint emp_actor_pk primary key (emp_no, actor_id) 
     , constraint emp_actor_emp_fk foreign key (emp_no) 
       references eployees (emp_no) 
     , constraint cust_actor_actor_fk foreign key (actor_id) 
       references actors (actor_id) 
    ) 
/

TASKS表是相當不奇怪,給出什麼是去NE前:

create table tasks 
    (task_code varchar2(4) not null 
    , actor_id number not null 
    , constraint task_pk primary key (task_code, actor_id) 
    , constraint task_task_fk ask foreign key (task_code) 
      references task_types (task_code) 
    , constraint task_actor_fk foreign key (actor_id) 
      references actors (actor_id) 
/

我同意所有這些路口表看起來像一個很大的開銷,但沒有任何其他的方式來強制執行外鍵約束。每當您在CUSTOMERS中創建記錄時,額外的障礙就是創建ACTORS和CUSTOMER_ACTORS記錄。同上刪除。唯一的好消息是你可以生成你需要的所有代碼。

此解決方案是否比具有一百個可選外鍵的表更好?也許不是:這是一個品味問題。但我喜歡它比沒有外鍵更好。如果在數據庫實踐中存在關於euniversal的真理,那麼它是這樣的:依靠應用程序代碼來強制執行關係完整性的數據庫是充斥着引用錯誤父項或根本不引用父項的子項的數據庫。

+0

我同意所有。另外,如果你想爲不同的水果執行不同的任務,你還需要'tasks'和'task_types'中的'fruit_type'和FKs爲:'Foreign Key(fruit_id,fruit_type)References fruit(fruit_id,fruit_type)'和'外鍵(task_code,fruit_type)引用task_types(task_code,fruit_type)' – 2012-01-11 13:11:29

+0

我的原始示例中沒有水果表。在我們的模型中添加一個沒有意義。爲了簡化這個例子,我只用了水果,但實際上這些表是完全不相關的概念(「客戶」,「房間」,「會議」,「薪水」),沒有共同的祖先表指向所有這些概念。儘管步驟列表需要能夠討論所有這些概念(「拜訪客戶」,「對建築物進行維護」,「發現工資> 100000美元」等)。 – StilesCrisis 2012-01-11 16:09:28

+0

我在想這種方法可能是我最好的選擇......我會盡量使它適應我實際得到的結果,看看它是否有效。如果我可以在某處找到答案,我會給予答案。感謝所有的細節。 – StilesCrisis 2012-01-11 17:40:40