Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

So verhindern Sie Aktualisierungen einer Tabelle, mit Ausnahme einer Situation

Warum nicht einen INSTEAD OF verwenden Abzug? Es erfordert etwas mehr Arbeit (nämlich ein wiederholtes UPDATE Anweisung), aber jederzeit, wenn Sie Arbeit verhindern können, werden Sie besser dran sein, anstatt sie geschehen zu lassen und sie dann rückgängig zu machen.

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

Das passt nicht perfekt. Die Kriterien, die ich weggelassen habe, werden aus einem bestimmten Grund weggelassen:Es kann komplex sein, festzustellen, ob sich ein Spaltenwert geändert hat, da dies vom Datentyp abhängt, ob die Spalte NULL sein kann usw. AFAIK, die eingebauten Triggerfunktionen kann nur feststellen, ob eine bestimmte Spalte angegeben wurde, nicht, ob sich der Wert tatsächlich geändert hat.

BEARBEITEN Wenn man bedenkt, dass Sie sich nur um die anderen Spalten sorgen, die aufgrund des After-Triggers aktualisiert werden, denke ich, dass das folgende INSTEAD OF Trigger kann Ihre beiden vorhandenen Trigger ersetzen und auch mehrere Zeilen gleichzeitig aktualisieren (einige ohne Ihre Kriterien zu erfüllen):

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