INSTEAD OF
を使用してみませんか 引き金?もう少し作業が必要です(つまり、UPDATE
を繰り返す必要があります 声明)しかし、あなたが仕事を防ぐことができるときはいつでも、それを起こさせてからそれをロールバックする代わりに、あなたはより良くなるでしょう。
CREATE TRIGGER [dbo].[Item_BeforeUpdate_AnyBilled]
ON [dbo].[Item]
INSTEAD OF UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS
(
SELECT 1 FROM inserted i
JOIN deleted AS d ON i.ItemId = d.ItemId
WHERE d.BillId IS NULL -- it was NULL before, may not be NULL now
)
BEGIN
UPDATE src
SET col1 = i.col1 --, ... other columns
ModifiedDate = CURRENT_TIMESTAMP -- this eliminates need for other trigger
FROM dbo.Item AS src
INNER JOIN inserted AS i
ON i.ItemId = src.ItemId
AND (criteria to determine if at least one column has changed);
END
ELSE
BEGIN
RAISERROR(...);
END
END
GO
これは完全には適合しません。私が省略した基準は、次の理由で省略されています。データ型、列をNULLにできるかどうかなどに依存するため、列の値が変更されたかどうかを判断するのは複雑になる可能性があります。組み込みのトリガー関数を確認してください。特定の列が指定されたかどうかのみがわかり、値が実際に以前から変更されたかどうかはわかりません。
編集 アフタートリガーによって更新される他の列のみを考慮していることを考えると、次のINSTEAD OF
トリガーは、既存の両方のトリガーを置き換えることができ、一度に更新された複数の行を処理することもできます(基準を満たさないものもあります):
CREATE TRIGGER [dbo].[Item_BeforeUpdate_AnyBilled]
ON [dbo].[Item]
INSTEAD OF UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE src SET col1 = i.col1 --, ... other columns,
ModifiedDate = CURRENT_TIMESTAMP
FROM dbo.Item AS src
INNER JOIN inserted AS i
ON src.ItemID = i.ItemID
INNER JOIN deleted AS d
ON i.ItemID = d.ItemID
WHERE d.BillID IS NULL;
IF @@ROWCOUNT = 0
BEGIN
RAISERROR(...);
END
END
GO