2012-10-30 67 views
0

我試圖利用SQL Server 2008R MERGE功能來管理連接表中的父子關係記錄。我可以創建一個虛擬INSTEAD OF UPDATE,INSERT觸發器,以便我可以使用MERGE和INSTEAD OF DELETE?

連接表表示多對多關係,所以它有兩個用於相同主鍵的外鍵。因此,不能使用ON DELETE CASCADE。在這裏,我使用了觸發器INSTEAD OF DELETE並刪除了連接記錄,因此刪除了約束條件,因此我可以完成最初預期的刪除操作。

不幸的是,當我嘗試在這種情況下使用MERGE時,出現以下錯誤。

The target 'Content' of the MERGE statement has an INSTEAD OF trigger on some, 
but not all, of the actions specified in the MERGE statement. In a MERGE statement, 
if any action has an enabled INSTEAD OF trigger on the target, then all actions 
must have enabled INSTEAD OF triggers. 

下面是用於重現此問題的T-SQL。爲了方便起見,我添加了註釋掉和選擇語句。

CREATE DATABASE [TestDatabase] 
GO 

USE [TestDatabase] 
GO 

CREATE TABLE dbo.[Content] (
    ContentID int NOT NULL IDENTITY (1, 1), 
    Title varchar(255) 
) 
ALTER TABLE dbo.[Content] 
    ADD CONSTRAINT PK_Content 
    PRIMARY KEY CLUSTERED (ContentID) 

CREATE TABLE dbo.[Attachment] (
    ParentContentID int NOT NULL, 
    ChildContentID int NOT NULL 
) 

ALTER TABLE [dbo].[Attachment] 
    ADD CONSTRAINT [PK_Attachment] 
    PRIMARY KEY CLUSTERED (
     [ParentContentID] ASC, 
     [ChildContentID] ASC 
    ) 

ALTER TABLE dbo.Attachment 
    ADD CONSTRAINT FK_Attachment_ParentContent 
    FOREIGN KEY (ParentContentID) 
    REFERENCES dbo.[Content] (ContentID) 
    ON UPDATE NO ACTION 
    ON DELETE NO ACTION 

ALTER TABLE dbo.Attachment 
    ADD CONSTRAINT FK_Attachment_ChildContent 
    FOREIGN KEY (ChildContentID) 
    REFERENCES dbo.[Content] (ContentID) 
    ON UPDATE NO ACTION 
    ON DELETE NO ACTION 
GO 

CREATE TRIGGER trContentInsteadOfDelete 
    ON dbo.[Content] 
    INSTEAD OF DELETE 
AS 

    SET NOCOUNT ON; 

    DELETE FROM dbo.[Attachment] 
    WHERE [ParentContentID] IN (SELECT [ContentID] FROM deleted) 
    OR [ChildContentID] IN (SELECT [ContentID] FROM deleted) 

    DELETE FROM dbo.[Content] 
    WHERE [ContentID] IN (SELECT [ContentID] FROM deleted) 
GO 

INSERT INTO [Content] ([Title]) VALUES ('a'), ('a'), ('a'), ('b') 
GO 

INSERT INTO [Attachment] ([ParentContentID], [ChildContentID]) 
    VALUES (1, 2), (1, 4), (3, 4) 
GO 

MERGE [Content] AS target 
USING (VALUES (1, 'a'), (2, 'b'), (NULL, 'b')) AS source ([ContentID], [Title]) 
ON target.[ContentID] = source.[ContentID] 
WHEN MATCHED AND target.[Title] != source.[Title] THEN 
    UPDATE SET target.[Title] = source.[Title] 
WHEN NOT MATCHED BY TARGET THEN 
    INSERT ([Title]) VALUES (source.[Title]) 
WHEN NOT MATCHED BY source THEN 
    DELETE; 

/* 
USE master 
DROP DATABASE [TestDatabase] 

SELECT * FROM [Content] 
SELECT * FROM [Attachment] 
*/; 

是否有任何替代品添加觸發器 INSTEAD OF INSERTINSTEAD OF UPDATE,而不必從內容表中刪除記錄之前明確地刪除附件表中的任何約束的記錄?

我可以暫時禁用觸發器,但是我必須顯式刪除附件表中的約束記錄。

我擔心添加額外的觸發器只是爲了適應MERGE語句而失敗了使用MERGE語句的目的。

更新:有沒有辦法創建一個虛擬INSTEAD的插入,更新觸發器,使我能夠繼續使用合併?

+1

我「不能確定你期望在這裏有什麼答案:錯誤是明確和要求[記錄](http://msdn.microsoft.com/en-us/library/bb510625(V = SQL .105).aspx)所以最好的你可以希望是一個解決方法,比如創建虛擬觸發器,它什麼都不做。 – Pondlife

+0

@Pondlife,我寧願保留使用MERGE的能力,並找到更好的方法來清理孤兒連接記錄在附件表中,我希望有人有更好的方法來達到我在上面的代碼中說明的相同結果,謝謝你的評論。 – Kuyenda

回答

0

這似乎有伎倆,但我想知道對性能的影響。

CREATE TRIGGER trContentInsteadOfDelete 
    ON dbo.[Content] 
    INSTEAD OF DELETE 
AS 

    SET NOCOUNT ON; 

    DELETE FROM dbo.[Attachment] 
    WHERE [ParentContentID] IN (SELECT [ContentID] FROM deleted) 
    OR [ChildContentID] IN (SELECT [ContentID] FROM deleted) 

    DELETE FROM dbo.[Content] 
    WHERE [ContentID] IN (SELECT [ContentID] FROM deleted) 

GO 

CREATE TRIGGER trContentInsteadOfInsertUpdate 
    ON dbo.[Content] 
    INSTEAD OF INSERT, UPDATE 
AS 

    SET NOCOUNT ON; 

    INSERT INTO dbo.[Content] (
     [Title] 
    ) 
    SELECT 
     i.[Title] 
    FROM inserted i 
    LEFT JOIN dbo.[Content] c ON i.[ContentID] = c.[ContentID] 
    WHERE c.[ContentID] IS NULL 

    UPDATE c 
    SET 
     c.[Title] = i.[Title] 
    FROM dbo.[Content] c 
    JOIN inserted i ON c.[ContentID] = i.[ContentID] 
GO 
相關問題