2011-06-23 18 views
3

複数のBillLinesを持つことができる複数のBillsを持つことができます。バッチを削除すると、関連するBillとBillLineのレコードも削除されるように、それらの間にON DELETE CASCADE FKがあります。請求書を削除すると、関連するBillLinesは削除されますが、バッチレコードは影響を受けません。これで、関連するBillLineレコードが1つ以上ある特定のデータ条件がある場合、Billの削除を防止する必要があります。削除の代わりにON DELETE CASCADE FKとのトリガーの競合

テーブルビルドには、INSTEAD OF DELETEトリガーが必要です。 BillLine.BillIdには、Bill.BillIdを参照するON DELETE CASCADE FKがあります。なぜなら、INSTEAD OF DELETEトリガーがCASCADEの機能を効果的に置き換えるため、FKが代わりにNO DELETE NO ACTIONを行う必要があるということです。請求書を削除すると、削除の代わりに関連付けられたBillLineレコードが削除されるか、特定のデータ条件に応じて例外が発生します。ここまでは順調ですね。

しかし、Bill.BatchIdにはBatch.BatchIdを参照するON DELETE CASCADE FKがあるため、SQL Serverはトリガーを作成させません。これは私が理解していない。なぜ私はBillに1つしかないので、バッチでINSTEAD OF DELETEトリガーを構築する必要がありますか?

以下の表とキーを作成するコード(無関係な列とキーはすべて省略)は、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 

あなたはCASCADE、ON DELETEでBatch_Deleteトリガーを交換しようとした場合:[FK_Bill_Batch_BatchId] DROP TRIGGER [Batch_Delete] ALTER TABLE [ビル] DROP制約を。 [バッチ]([BatchId])ON DELETE CASCADE; [BUCH] [BUCH]

あなたはこれを取得します:ONビル・テーブルにINSTEAD OF DELETEトリガーとは何かを持っている必要があり、この方向に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. 

+0

create table statementsを転記できますか?私が知っていることから、SQL Serverでは、親テーブルではなく、子テーブルに対して「INSTEAD OF」トリガーを作成させることはできません。 – a1ex07

+0

私はコードを追加しました。新しいデータベースを作成して実行するだけで、テーブル、キー、トリガーを作成できます。データを追加する必要はありません。 –

+0

これはあなたの質問とは別のものですが、トリガーでcusrorを使用しないでください。大きなグループのレコードが削除されると、大きなパフォーマンス上の問題が発生する可能性があります。 – HLGEM

答えて

0

私の考えでは、INSTEAD OFトリガは必要ありません。 INSTEAD OFトリガは、操作の代わりに常に動作します。 場合によっては例外をスローしたい場合があります。

したがって、DELETE CASCADEと通常の(AFTER)トリガーを使用できます。

例外の中でRAISERRORを実行すると、おそらくROLLBACKトランザクションが実行されます。 (常にトリガの周りに暗黙的なトランザクションがあります)

関連する問題