2012-10-30 139 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 
相关问题