1

我正在尝试利用 SQL Server 2008R MERGE 功能来管理连接表中的父子关系记录。

连接表表示多对多关系,因此它有两个外键对应同一个主键。因此,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。为方便起见,我已将注释掉的 drop 和 select 语句包括在内。

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]
*/;

在从 Content 表中删除记录之前,是否有任何替代方法可以添加触发器INSTEAD OF INSERT并且INSTEAD OF UPDATE不必显式删除 Attachment 表中的任何约束记录?

我可以暂时禁用触发器,但随后我必须明确删除附件表中的约束记录。

我担心添加额外的触发器只是为了适应 MERGE 语句会破坏使用 MERGE 语句的目的。

更新:有没有办法创建一个虚拟 INSTEAD OF INSERT, UPDATE 触发器以使我能够继续使用 MERGE?

4

1 回答 1

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
于 2012-10-31T22:36:06.550 回答