Dlaczego nie użyć INSTEAD OF
cyngiel? Wymaga trochę więcej pracy (a mianowicie powtarzania UPDATE
oświadczenie), ale za każdym razem, gdy możesz uniemożliwić pracę, zamiast pozwolić jej się wydarzyć, a następnie ją wycofać, wyjdzie ci lepiej.
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
To nie pasuje idealnie. Kryteria, które pominąłem, zostały pominięte z jakiegoś powodu:ustalenie, czy wartość kolumny uległa zmianie, może być skomplikowane, ponieważ zależy to od typu danych, czy kolumna może mieć wartość NULL itp. AFAIK wbudowane funkcje wyzwalacza może tylko stwierdzić, czy określona kolumna została określona, a nie, czy wartość rzeczywiście zmieniła się w stosunku do poprzedniej.
EDYTUJ biorąc pod uwagę, że martwisz się tylko innymi kolumnami, które są aktualizowane z powodu wyzwalacza po, myślę, że następujące INSTEAD OF
wyzwalacz może zastąpić oba istniejące wyzwalacze, a także obsługiwać wiele wierszy aktualizowanych jednocześnie (niektóre bez spełniania kryteriów):
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