Varför inte använda en INSTEAD OF
utlösa? Det kräver lite mer arbete (nämligen en upprepad UPDATE
uttalande) men varje gång du kan förhindra arbete, istället för att låta det hända och sedan rulla tillbaka det, kommer du att ha det bättre.
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
Det här passar inte perfekt. Kriterierna jag har utelämnat utelämnas av en anledning:det kan vara komplicerat att avgöra om ett kolumnvärde har ändrats, eftersom det beror på datatypen, om kolumnen kan vara NULL, etc. AFAIK den inbyggda triggern fungerar kan bara avgöra om en viss kolumn angavs, inte om värdet faktiskt ändrades från tidigare.
REDIGERA med tanke på att du bara är orolig för de andra kolumnerna som uppdateras på grund av efterutlösaren, tror jag följande INSTEAD OF
trigger kan ersätta båda dina befintliga triggers och även hantera flera rader uppdaterade samtidigt (vissa utan att uppfylla dina kriterier):
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