1

SQL Server 2008R MERGE 機能を利用して、結合テーブル内の親子関係レコードを管理しようとしています。

結合テーブルは多対多の関係を表すため、同じ主キーに対して 2 つの外部キーがあります。したがって、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]
*/;

INSTEAD OF INSERTContent テーブルからレコードを削除する前に、Attachment テーブル内の制約レコードを明示的に削除せ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 に答える