Considerazioni sulle istruzioni che interessano più righe

Quando si scrive il codice di un trigger DML, è importante considerare che l'istruzione che attiva il trigger può essere singola e interessare più righe di dati, anziché una sola riga. Questo funzionamento è comune per i trigger UPDATE e DELETE perché queste istruzioni in genere interessano più righe, mentre è meno comune per i trigger INSERT perché l'istruzione INSERT di base aggiunge soltanto una riga singola. Dato però che un trigger INSERT può essere attivato da un'istruzione INSERT INTO (table_name) SELECT, l'inserimento di molte righe può causare la chiamata a un unico trigger.

Queste considerazioni sono di particolare importanza quando la funzione di un trigger DML consiste nel ricalcolare automaticamente i valori di riepilogo di una tabella e archiviare i risultati in un'altra tabella per i conteggi.

[!NOTA] Non è consigliabile utilizzare cursori nei trigger dato che possono potenzialmente ridurre le prestazioni. Per progettare un trigger che interessa più righe, utilizzare una logica basata su set di righe invece che su cursori.

Esempi

I trigger DML negli esempi seguenti sono progettati per archiviare il totale parziale di una colonna in un'altra tabella del database di esempio di AdventureWorks.

A. Archiviazione di un totale parziale per l'inserimento di una riga singola

La prima versione del trigger DML funziona correttamente per l'inserimento di una singola riga, quando una riga di dati viene caricata nella tabella PurchaseOrderDetail. Il trigger DML viene attivato da un'istruzione INSERT e la nuova riga viene caricata nella tabella inserted per la durata dell'esecuzione del trigger. L'istruzione UPDATE legge il valore della colonna LineTotal per la riga e lo aggiunge al valore esistente nella colonna SubTotal della tabella PurchaseOrderHeader. La clausola WHERE verifica che la riga aggiornata nella tabella PurchaseOrderDetail corrisponda al valore di PurchaseOrderID della riga nella tabella inserted.

-- Trigger is valid for single-row inserts.
USE AdventureWorks;
GO
CREATE TRIGGER NewPODetail
ON Purchasing.PurchaseOrderDetail
AFTER INSERT AS
   UPDATE PurchaseOrderHeader
   SET SubTotal = SubTotal + LineTotal
   FROM inserted
   WHERE PurchaseOrderHeader.PurchaseOrderID = inserted.PurchaseOrderID ;

B. Archiviazione di un totale parziale per l'inserimento di una riga singola o di più righe

Nel caso di un'istruzione che interessa più righe, il trigger DML dell'esempio A potrebbe non funzionare correttamente. Nell'espressione a destra di un'espressione di assegnazione in un'istruzione UPDATE (SubTotal + LineTotal) è possibile includere un valore singolo, non un elenco di valori. L'effetto del trigger è quindi quello di recuperare un valore dalle righe singole della tabella inserted e aggiungerlo al valore esistente SubTotal nella tabella PurchaseOrderHeader per un determinato valore di PurchaseOrderID. L'effetto dell'operazione potrebbe non essere quello atteso se un singolo valore di PurchaseOrderID compare più volte nella tabella inserted.

Per aggiornare la tabella PurchaseOrderHeader correttamente, il trigger deve tener conto della possibile presenza di più righe nella tabella inserted. Ciò può essere ottenuto utilizzando la funzione SUM per calcolare il valore LineTotal totale relativo a un gruppo di righe nella tabella inserted per ogni PurchaseOrderID. La funzione SUM viene inclusa in una subquery correlata (l'istruzione SELECT in parentesi). Questa subquery restituisce un singolo valore per ogni PurchaseOrderID nella tabella inserted che corrisponde o è correlato a un valore PurchaseOrderID nella tabella PurchaseOrderHeader.

-- Trigger is valid for multirow and single-row inserts.
USE AdventureWorks;
GO
CREATE TRIGGER NewPODetail2
ON Purchasing.PurchaseOrderDetail
AFTER INSERT AS
   UPDATE PurchaseOrderHeader
   SET SubTotal = SubTotal + 
      (SELECT SUM(LineTotal)
      FROM inserted
      WHERE PurchaseOrderHeader.PurchaseOrderID
       = inserted.PurchaseOrderID)
   WHERE PurchaseOrderHeader.PurchaseOrderID IN
      (SELECT PurchaseOrderID FROM inserted);

Questo trigger funziona inoltre correttamente per l'inserimento di una riga singola. La somma dei valori della colonna LineTotal corrisponde alla somma di una riga singola. Con questo trigger, tuttavia, la subquery correlata e l'operatore IN utilizzato nella clausola WHERE comportano un'ulteriore elaborazione in SQL Server 2005, non necessaria per l'inserimento di una riga singola.

C. Archiviazione di un totale parziale sulla base del tipo di inserimento

È possibile modificare il trigger per utilizzare il metodo ottimale in base al numero di righe. È ad esempio possibile utilizzare la funzione @@ROWCOUNT nella logica del trigger per distinguere tra l'inserimento di una riga singola e di più righe.

-- Trigger valid for multirow and single row inserts
-- and optimal for single row inserts.
USE AdventureWorks;
GO
CREATE TRIGGER NewPODetail3
ON Purchasing.PurchaseOrderDetail
FOR INSERT AS
IF @@ROWCOUNT = 1
BEGIN
   UPDATE PurchaseOrderHeader
   SET SubTotal = SubTotal + LineTotal
   FROM inserted
   WHERE PurchaseOrderHeader.PurchaseOrderID = inserted.PurchaseOrderID

END
ELSE
BEGIN
      UPDATE PurchaseOrderHeader
   SET SubTotal = SubTotal + 
      (SELECT SUM(LineTotal)
      FROM inserted
      WHERE PurchaseOrderHeader.PurchaseOrderID
       = inserted.PurchaseOrderID)
   WHERE PurchaseOrderHeader.PurchaseOrderID IN
      (SELECT PurchaseOrderID FROM inserted)
END;

Vedere anche

Concetti

Implementazione di trigger DML

Guida in linea e informazioni

Assistenza su SQL Server 2005