2009-04-15 141 views
3

我們有一個兩對一的關係表。我們希望強制執行一個約束條件,即給定的父記錄至少存在一個子記錄。一對多關係約束

這可能嗎?

如果不是,你會改變模式有點複雜,以支持這種約束?如果是的話,你會怎麼做?

編輯:我使用的是SQL Server 2005中

+0

你沒有說你正在使用什麼DBMS。 – 2009-04-15 15:37:08

回答

7

這樣的限制是不可能的從架構的角度來看,因爲你碰到一個「先有雞還是先有蛋」式的情景。在這種情況下,當我插入到父表中時,我必須在子表中有一行,但在父表中有一行之前,我不能在子表中有一行。

這是更好的執行客戶端。

1

如果您的後端支持可延遲約束,PostgreSQL也是如此。

+0

就像Oracle – 2009-04-15 15:36:28

0

一個簡單的不可空列如何?

Create Table ParentTable 
(
ParentID 
ChildID not null, 
Primary Key (ParentID), 
Foreign Key (ChildID) references Childtable (ChildID)); 
) 

如果你的業務邏輯允許,你有,你可以從數據庫中爲每個新的父記錄查詢默認值,然後你可以使用一個before insert trigger父表填充不爲空的子列。

CREATE or REPLACE TRIGGER trigger_name 
BEFORE INSERT 
    ON ParentTable 
    FOR EACH ROW 
BEGIN 

    -- (insert new row into ChildTable) 
    -- update childID column in ParentTable 

END; 
+0

這不允許一對多的關係。 – mkb 2009-04-15 15:18:26

0

這真的不是那麼多,因爲它是什麼,是不切實際的某些數據庫實現內實施「在客戶端執行好」。實際上,DOES屬於數據庫,下面至少應該有一個解決方法。

最終你想要的是將父母約束到一個孩子。這保證了孩子的存在。不幸的是,這導致雞蛋問題,因爲孩子們必須指向導致約束衝突的同一父母。

在系統的其他部分避免出現可見的副作用需要使用兩種功能之一 - 在SQL Server中都沒有發現這兩種功能。

1)延遲約束驗證 - 這會導致約束在事務結束時進行驗證。通常他們發生在聲明的最後。這是雞蛋問題的根源,因爲它阻止你插入第一個孩子或父母行,因爲缺乏另一個,這解決了它。

2)您可以使用CTE插入CTE掛起插入父級的語句(或反之亦然)的第一個孩子。這會在同一個語句中插入兩行,從而導致類似於延遲約束驗證的效果。

3)如果沒有其他選擇,只能在其中一個引用中允許空值,以便您可以插入該行而不進行依賴性檢查。然後,您必須返回並更新空引用第二行。如果您使用這種技術,則需要小心使系統的其餘部分通過隱藏子引用列中所有包含null的行的視圖引用父表。

在任何情況下,您刪除兒童的也一樣複雜,因爲您無法刪除證明至少存在一個孩子的孩子,除非您先更新父母以指向不會被刪除的孩子。

當您要刪除最後一個孩子時,您必須拋出錯誤或同時刪除父母。如果您沒有首先將父指針設置爲空(或推遲驗證),則會自動發生錯誤。如果您確實延遲(或將子指針設置爲空),則可以刪除子項,然後可以刪除父項。

我從字面上研究了這麼多年,並且我觀察了SQL Server的每個版本以解決這個問題,因爲它很常見。

只要有人有一個實用的解決方案,請張貼!

P.S.您需要在從父級引用您的子證明行或觸發器時使用複合鍵,以確保提供證明的孩子實際上認爲該行是其父級。

P.P.S雖然如果你在同一個事務中同時插入和更新,那麼系統的其餘部分永遠不會看到null,這依賴於可能失敗的行爲。約束的要點是確保邏輯故障不會使數據庫處於無效狀態。通過隱藏隱藏視圖來保護表格,任何非法行都將不可見。很明顯,你的插入邏輯必須考慮到這樣的行可能存在的可能性,但無論如何它需要內部知識,而沒有別的東西需要知道。

0

我遇到了這個問題,並在Oracle rel.11.2.4中實現了一個解決方案。

  1. 爲了確保每個孩子都有父母,我將孩子的FK中的典型外鍵約束應用於父母的PK。 - 在這裏
  2. 沒有魔力爲了確保每一個家長至少有一個孩子,我做了如下:

創建,它接受一個父PK,並返回兒童對於PK的計數功能。 - 我確保NO_DATA_FOUND異常返回0.

在父表上創建虛擬列CHILD_COUNT並將其計算爲函數結果。

CHILD_COUNT > 0

的標準其工作原理如下創建的CHILD_COUNT虛擬列一個延遲的CHECK約束:

  • 如果父行插入,並沒有孩子還不存在。那麼如果該行被提交,那麼CHECK約束失敗並且事務回滾。
  • 如果插入父行並在相同的事務中插入相應的子行,那麼當發出COMMIT時,所有完整性約束都得到滿足。
  • 如果插入的子行對應於現有的父行,則將在COMMIT上重新計算CHILD_COUNT虛擬列,並且不會發生完整性違例。
  • 任何父行的刪除必須級聯到子級,否則當刪除事務提交時,孤行的子行將違反FOREIGN KEY約束。 (照常)
  • 任何刪除的子行必須爲每個父項留至少一個子項,否則在事務提交時CHILD_COUNT檢查約束將違反。

注意:如果Oracle允許在基於用戶函數的CHECK constraints位於rel.11.2.4,那麼我不需要虛擬列。