7

在SQL-Server中處理自引用外鍵約束的推薦方式是什麼?自引用外鍵約束和刪除

表 - 型號:

enter image description here

fiData引用在tabData以前的紀錄。如果我刪除由fiData引用的記錄,該數據庫將引發異常:

「DELETE語句衝突以引用同一表 約束‘FK_tabDataPrev_tabDataNext’。該衝突發生於 數據庫‘MyDatabase的’,表「dbo.tabData」,列'fiData'「

如果Enforce Foreignkey Constraint設置爲」是「。

我不需要級聯刪除被引用的記錄,但我需要在其引用的位置設置fiData=NULL。我的想法是將Enforce Foreignkey Constraint設置爲「否」並創建刪除觸發器。這是值得推薦的還是有更好的方法?

謝謝。

回答

7

不像Andomar,我很樂意使用觸發器 - 但我不會刪除約束檢查。如果要實現它作爲一個instead of觸發,你可以在執行之前,其他行空重實際刪除:

CREATE TRIGGER T_tabData_D 
on tabData 
instead of delete 
as 
    set nocount on 
    update tabData set fiData = null where fiData in (select idData from deleted) 
    delete from tabData where idData in (select idData from deleted) 

它的短,它的簡潔,它不會是必要的,如果SQL服務器可以處理外鍵級聯到同一個表(在其他RDBMS中,您可能只需指定ON DELETE SET NULL作爲外鍵約束YMMV)。

+1

謝謝。但是我在創建觸發器時遇到了一個異常:「INSTEAD OF DELETE/UPDATE觸發器無法在具有DELETE/UPDATE動作定義的級聯的外鍵的表上定義」。 – 2011-03-24 12:31:04

+0

我知道這個評論太遲了,但對於像我這樣搜索的人來說。 這隻會刪除一個條目。它不會遞歸地級聯。 – Arif 2012-10-05 11:56:39

+0

@Arif - 這是「ON DELETE SET NULL」,而不是「ON DELETE CASCADE」。前者從不需要遞歸。對於「ON DELETE CASCADE」,我推薦一個CTE,它首先計算所有「ID」值的閉包,然後執行刪除操作。 – 2012-10-05 11:59:59

2

觸發器添加隱含的複雜性。在一個帶有觸發器的數據庫中,通過查看它,你不會知道SQL語句的作用。根據我的經驗,觸發器是個不錯的主意,沒有例外。

在您的示例中,將強制約束設置爲「否」意味着您可以添加不存在的ID。而且查詢優化器效率不高,因爲它不能假定密鑰是有效的。

考慮創建一個存儲過程,而不是:

create procedure dbo.NukeTabData(
    @idData int) 
as 
begin transaction 
update tabData set fiData = null where fiData = @idData 
delete from tabData where idData = @idData 
commit transaction 
go 
+3

現在你的調用代碼有'exec NukeTabData(ID)',你不知道沒有檢查過程內容會發生什麼。爲什麼沒有,當一個觸發器不是? – 2011-03-24 10:49:06

+2

@Damien_The_Unbeliever:存儲過程是明確的:如果我對細節感興趣,它告訴我應該在哪裏看。一個觸發器是隱含的:「從tabData刪除idData = 42'暗示存在將運行的附加更新。例如,在運行delete之後,觸發器可能會使'@@ rowcount'爲4而不是1.這導致了一些棘手的錯誤 - 特別是對於新的代碼庫開發人員。 – Andomar 2011-03-24 10:54:00

+0

@@ ROWCOUNT是基於範圍的 - 它不受觸發器內的任何活動影響。你可能會想到@@ IDENTITY,但大多數人知道現在要避免這種情況 – 2011-03-24 11:18:22

0

這很晚纔回答。

但是對於像我這樣尋找的人來說。

,並希望cascade

這裏很好解釋

http://devio.wordpress.com/2008/05/23/recursive-delete-in-sql-server/

的問題 雖然你可以定義CASCADE外鍵在SQL服務器中刪除,遞歸級聯刪除,不支持(即在同一個表上級聯刪除)。

如果您創建INSTEAD OF DELETE觸發器,則此觸發器僅觸發第一個DELETE語句,並且不會觸發從此觸發器遞歸刪除的記錄。

此行爲是在MSDN上記錄的SQL Server 2000和SQL Server 2005

解決方案 假設你有一個像這樣定義的表:

CREATE TABLE MyTable (
    OID INT,  -- primary key 
    OID_Parent INT, -- recursion 
    ... other columns 
) 

然後刪除觸發器看起來是這樣的:

CREATE TRIGGER del_MyTable ON MyTable INSTEAD OF DELETE 
AS 
    CREATE TABLE #Table(
     OID INT 
    ) 
INSERT INTO #Table (OID) 
SELECT OID 
FROM deleted 

DECLARE @c INT 
SET @c = 0 

WHILE @c <> (SELECT COUNT(OID) FROM #Table) BEGIN 
    SELECT @c = COUNT(OID) FROM #Table 

    INSERT INTO #Table (OID) 
    SELECT MyTable.OID 
    FROM MyTable 
    LEFT OUTER JOIN #Table ON MyTable.OID = #Table.OID 
    WHERE MyTable.OID_Parent IN (SELECT OID FROM #Table) 
    AND  #Table.OID IS NULL 
END 

DELETE MyTable 
FROM MyTable 
INNER JOIN #Table ON MyTable.OID = #Table.OID 

GO 
+0

這是一個很好的答案ON DELETE CASCADE,但問題是關於ON DELETE SET NULL – dajoropo 2016-11-15 09:22:59