Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Come impedire gli aggiornamenti a una tabella, con un'eccezione per una situazione

Perché non usare un INSTEAD OF grilletto? Richiede un po' più di lavoro (vale a dire un UPDATE ripetuto dichiarazione) ma ogni volta che puoi impedire il lavoro, invece di lasciare che accada e poi annullarlo, starai meglio.

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

Questo non si adatta perfettamente. I criteri che ho omesso sono stati omessi per un motivo:può essere complesso determinare se il valore di una colonna è cambiato, poiché dipende dal tipo di dati, se la colonna può essere NULL, ecc. AFAIK le funzioni di trigger integrate può dire solo se è stata specificata una determinata colonna, non se il valore è effettivamente cambiato rispetto a prima.

MODIFICA considerando che sei preoccupato solo per le altre colonne che vengono aggiornate a causa dell'attivazione successiva, penso che il seguente INSTEAD OF trigger può sostituire entrambi i trigger esistenti e anche gestire più righe aggiornate contemporaneamente (alcune senza soddisfare i tuoi criteri):

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