我們有一個兩對一的關係表。我們希望強制執行一個約束條件,即給定的父記錄至少存在一個子記錄。一對多關係約束
這可能嗎?
如果不是,你會改變模式有點複雜,以支持這種約束?如果是的話,你會怎麼做?
編輯:我使用的是SQL Server 2005中
我們有一個兩對一的關係表。我們希望強制執行一個約束條件,即給定的父記錄至少存在一個子記錄。一對多關係約束
這可能嗎?
如果不是,你會改變模式有點複雜,以支持這種約束?如果是的話,你會怎麼做?
編輯:我使用的是SQL Server 2005中
這樣的限制是不可能的從架構的角度來看,因爲你碰到一個「先有雞還是先有蛋」式的情景。在這種情況下,當我插入到父表中時,我必須在子表中有一行,但在父表中有一行之前,我不能在子表中有一行。
這是更好的執行客戶端。
如果您的後端支持可延遲約束,PostgreSQL也是如此。
就像Oracle – 2009-04-15 15:36:28
一個簡單的不可空列如何?
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;
這不允許一對多的關係。 – mkb 2009-04-15 15:18:26
這真的不是那麼多,因爲它是什麼,是不切實際的某些數據庫實現內實施「在客戶端執行好」。實際上,DOES屬於數據庫,下面至少應該有一個解決方法。
最終你想要的是將父母約束到一個孩子。這保證了孩子的存在。不幸的是,這導致雞蛋問題,因爲孩子們必須指向導致約束衝突的同一父母。
在系統的其他部分避免出現可見的副作用需要使用兩種功能之一 - 在SQL Server中都沒有發現這兩種功能。
1)延遲約束驗證 - 這會導致約束在事務結束時進行驗證。通常他們發生在聲明的最後。這是雞蛋問題的根源,因爲它阻止你插入第一個孩子或父母行,因爲缺乏另一個,這解決了它。
2)您可以使用CTE插入CTE掛起插入父級的語句(或反之亦然)的第一個孩子。這會在同一個語句中插入兩行,從而導致類似於延遲約束驗證的效果。
3)如果沒有其他選擇,只能在其中一個引用中允許空值,以便您可以插入該行而不進行依賴性檢查。然後,您必須返回並更新空引用第二行。如果您使用這種技術,則需要小心使系統的其餘部分通過隱藏子引用列中所有包含null的行的視圖引用父表。
在任何情況下,您刪除兒童的也一樣複雜,因爲您無法刪除證明至少存在一個孩子的孩子,除非您先更新父母以指向不會被刪除的孩子。
當您要刪除最後一個孩子時,您必須拋出錯誤或同時刪除父母。如果您沒有首先將父指針設置爲空(或推遲驗證),則會自動發生錯誤。如果您確實延遲(或將子指針設置爲空),則可以刪除子項,然後可以刪除父項。
我從字面上研究了這麼多年,並且我觀察了SQL Server的每個版本以解決這個問題,因爲它很常見。
請只要有人有一個實用的解決方案,請張貼!
P.S.您需要在從父級引用您的子證明行或觸發器時使用複合鍵,以確保提供證明的孩子實際上認爲該行是其父級。
P.P.S雖然如果你在同一個事務中同時插入和更新,那麼系統的其餘部分永遠不會看到null,這依賴於可能失敗的行爲。約束的要點是確保邏輯故障不會使數據庫處於無效狀態。通過隱藏隱藏視圖來保護表格,任何非法行都將不可見。很明顯,你的插入邏輯必須考慮到這樣的行可能存在的可能性,但無論如何它需要內部知識,而沒有別的東西需要知道。
我遇到了這個問題,並在Oracle rel.11.2.4中實現了一個解決方案。
創建,它接受一個父PK,並返回兒童對於PK的計數功能。 - 我確保NO_DATA_FOUND異常返回0.
在父表上創建虛擬列CHILD_COUNT
並將其計算爲函數結果。
與CHILD_COUNT > 0
的標準其工作原理如下創建的CHILD_COUNT
虛擬列一個延遲的CHECK約束:
COMMIT
時,所有完整性約束都得到滿足。COMMIT
上重新計算CHILD_COUNT
虛擬列,並且不會發生完整性違例。CHILD_COUNT
檢查約束將違反。注意:如果Oracle允許在基於用戶函數的CHECK constraints
位於rel.11.2.4,那麼我不需要虛擬列。
你沒有說你正在使用什麼DBMS。 – 2009-04-15 15:37:08