Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Comment empêcher les mises à jour d'une table, à l'exception d'une situation

Pourquoi ne pas utiliser un INSTEAD OF gâchette? Cela nécessite un peu plus de travail (à savoir un UPDATE répété déclaration) mais chaque fois que vous pouvez empêcher le travail, au lieu de le laisser se produire puis de le faire reculer, vous vous porterez mieux.

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

Cela ne correspond pas parfaitement. Le critère que j'ai laissé de côté est laissé de côté pour une raison :il peut être complexe de déterminer si une valeur de colonne a changé, car cela dépend du type de données, si la colonne peut être NULL, etc. AFAIK les fonctions de déclenchement intégrées peut seulement dire si une certaine colonne a été spécifiée, pas si la valeur a réellement changé depuis avant.

MODIFIER étant donné que vous n'êtes préoccupé que par les autres colonnes qui sont mises à jour en raison du déclencheur après, je pense que le suivant INSTEAD OF peut remplacer vos deux déclencheurs existants et également traiter plusieurs lignes mises à jour à la fois (certaines sans répondre à vos critères) :

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