2011-06-23 90 views
3

批可以有多個Bills可以有多個BillLines。我已在他們之間刪除CASCADE FK,這樣如果刪除批處理,則相關的Bill和BillLine記錄也會被刪除。如果您刪除了賬單,則關聯的BillLines將被刪除,但批次記錄不受影響。現在,如果某個數據條件與一個或多個關聯的BillLine記錄存在關聯,則需要防止刪除該Bill。INSTEAD OF DELETE與ON DELETE CASCADE FK的觸發衝突

表比爾顯然需要INSTEAD OF DELETE觸發器。 BillLine.BillId具有引用Bill.BillId的ON DELETE CASCADE FK。這很有意義,因爲INSTEAD OF DELETE觸發器有效地替代了CASCADE功能,所以我需要將該FK設置爲​​ON DELETE NO ACTION。當我刪除賬單時,INSTEAD OF DELETE將根據特定的數據條件刪除相關的BillLine記錄或引發異常。到現在爲止還挺好。

但是,因爲Bill.BatchId具有引用Batch.BatchId的ON DELETE CASCADE FK,所以SQL Server不會讓我創建觸發器。這我不明白。爲什麼我需要在Batch上創建一個INSTEAD OF DELETE觸發器,僅僅是因爲我有一個針對Bill的?

下面創建表和鍵的代碼(省略了所有無關的列和鍵)是現在的情況,沒有ON DELETE CASCADE子句。問題是,爲什麼FK_Bill_Batch_BatchId不能擁有該子句,而不必創建額外的INSTEAD OF DELETE觸發器?

CREATE TABLE [Batch](
    [BatchId] [bigint] NOT NULL, 
CONSTRAINT [PK_Batch_BatchId] PRIMARY KEY CLUSTERED 
(
    [BatchId] ASC 
) 
) 

CREATE TABLE [Bill](
    [BillId] [bigint] NOT NULL, 
    [BatchId] [bigint] NOT NULL, 
    [ReversesBillId] [bigint] NULL, 
CONSTRAINT [PK_Bill_BillId] PRIMARY KEY CLUSTERED 
(
    [BillId] ASC 
) 
) 

ALTER TABLE [Bill] WITH CHECK ADD CONSTRAINT [FK_Bill_Batch_BatchId] FOREIGN KEY([BatchId]) 
REFERENCES [Batch] ([BatchId]) 

ALTER TABLE [Bill] WITH NOCHECK ADD CONSTRAINT [FK_Bill_ReversesBillId] FOREIGN KEY([ReversesBillId]) 
REFERENCES [Bill] ([BillId]) 

CREATE TABLE [BillLine](
    [BillLineId] [bigint] NOT NULL, 
    [BillId] [bigint] NOT NULL, 
    [ReversedByBillLineId] [bigint] NULL, 
CONSTRAINT [PK_BillLine_BillLineId] PRIMARY KEY CLUSTERED 
(
    [BillLineId] ASC 
) 
) 

ALTER TABLE [BillLine] WITH CHECK ADD CONSTRAINT [FK_BillLine_Bill_BillId] FOREIGN KEY([BillId]) 
REFERENCES [Bill] ([BillId]) 

ALTER TABLE [BillLine] WITH CHECK ADD CONSTRAINT [FK_BillLine_ReversedByBillLineId] FOREIGN KEY([ReversedByBillLineId]) 
REFERENCES [BillLine] ([BillLineId]) 
GO 

CREATE TRIGGER [Bill_Delete] 
    ON [Bill] 
    INSTEAD OF DELETE 
AS 
BEGIN 
    SET NOCOUNT ON 
    DECLARE @BillId UNIQUEIDENTIFIER 

    DECLARE myCursor CURSOR LOCAL FORWARD_ONLY 
     FOR SELECT b.[BillId] 
       FROM deleted b 
         JOIN [Batch] bt on b.[BatchId] = bt.[BatchId] 
    OPEN myCursor 
    FETCH NEXT FROM myCursor INTO @BillId 
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
     -- Delete BillLine records reversed by another BillLine in the same Bill 
     DELETE FROM [BillLine] 
       WHERE [BillId] = @BillId 
       AND [ReversedByBillLineId] IN 
         (SELECT bl.[BillLineId] 
          FROM [BillLine] bl 
         WHERE bl.BillId = @BillId 
         ); 

     -- Delete all remaining BillLine records for the Bill 
     -- If the BillLine is reversed by a BillLine in a different Bill, the FK will raise an exception. 
     -- That is the desired behavior. 
     DELETE FROM [BillLine] 
       WHERE [BillId] = @BillId; 

     -- Delete the Bill 
     DELETE FROM [Bill] 
       WHERE [BillId] = @BillId; 

     FETCH NEXT FROM myCursor INTO @BillId 
    END 
END 
GO 
CREATE TRIGGER [Batch_Delete] 
    ON [Batch] 
    INSTEAD OF DELETE 
AS 
BEGIN 
    SET NOCOUNT ON 
    DECLARE @BatchId UNIQUEIDENTIFIER 

    DECLARE myCursor CURSOR LOCAL FORWARD_ONLY 
     FOR SELECT [BatchId] 
       FROM deleted 
    OPEN myCursor 
    FETCH NEXT FROM myCursor INTO @BatchId 
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
     -- Delete all Bill records for the Batch. 
     -- Another INSTEAD OF DELETE trigger on Bill will attempt to delete the associated BillLine records in the correct order. 
     -- If the BillLine is reversed by a BillLine in a different Bill, FK_BillLine_ReversedByBillLineId will raise an exception. 
     -- That is the desired behavior. 
     DELETE FROM [Bill] 
       WHERE [BatchId] = @BatchId; 

     FETCH NEXT FROM myCursor INTO @BatchId 
    END 
END 

如果你嘗試更換Batch_Delete觸發與ON DELETE CASCADE: DROP TRIGGER [Batch_Delete] ALTER TABLE [草案] DROP CONSTRAINT [FK_Bill_Batch_BatchId] ALTER TABLE [Bill] WITH CHECK ADD CONSTRAINT [FK_Bill_Batch_BatchId] FOREIGN KEY([BatchId]) REFERENCES [Batch]([BatchId])ON DELETE CASCADE;

你會得到這樣的:

Msg 1787, Level 16, State 0, Line 2 
Cannot define foreign key constraint 'FK_Bill_Batch_BatchId' with cascaded DELETE or UPDATE on table 'Bill' because the table has an INSTEAD OF DELETE or UPDATE TRIGGER defined on it. 
Msg 1750, Level 16, State 0, Line 2 
Could not create constraint. See previous errors. 

我不明白爲什麼一個在這個方向DELETE CASCADE應該有什麼關係條例草案表中的INSTEAD OF DELETE觸發器。

+0

你可以發佈你的create table語句嗎?據我所知,SQL Server不允許爲子表創建'INSTEAD OF'觸發器,而不是爲父項創建。 – a1ex07

+0

我加了代碼。你可以創建一個新的數據庫並運行它來創建表,鍵和觸發器。無需添加任何數據。 –

+0

這是您的問題,但不要在觸發器中使用cusror,如果一大組記錄被刪除,這可能會造成巨大的性能問題。 – HLGEM

回答

0

在我看來,你不需要一個INSTEAD OF觸發器。 INSTEAD觸發器始終工作而不是操作。 您想在某些情況下拋出異常 - 並在其他情況下刪除。

所以你可以使用DELETE CASCADE和一個正常的(AFTER)觸發器。

在觸發器內部,您可以RAISERROR異常並可能ROLLBACK您的事務。 (觸發器總是存在隱式事務)