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

Trigger di SQL Server:Trigger DML

In SQL Server, i trigger sono oggetti di database che vengono eseguiti ogni volta che si verifica un evento di trigger sul database o sul server.

I trigger svolgono un ruolo chiave nel raggiungimento di requisiti aziendali come l'avviso di persone mirate, l'avvio di un lavoro o altre operazioni. Poiché Trigger può gestire molte di queste operazioni, dovremmo definirle con attenzione per evitare impatti sulle prestazioni.

In questo articolo esamineremo i Trigger, i tipi di Trigger e le varie opzioni di Trigger disponibili. Inoltre, esploreremo le precauzioni necessarie durante l'utilizzo dei trigger DML.

Trigger in SQL

Un Trigger è un tipo speciale di stored procedure che viene eseguito su eventi definiti, eseguendo lo script definito nel corpo del Trigger. Esistono diversi tipi di trigger:

  • Trigger DML – per eseguire operazioni DML come i comandi INSERT, UPDATE e DELETE sulle tabelle.
  • Trigger DDL – per eseguire operazioni DDL come i comandi CREATE, ALTER e DROP su qualsiasi oggetto nel database o nel server.
  • Trigger di accesso – per il tentativo di accesso a un'istanza di SQL Server durante l'evento LOGON.

Trigger DML in SQL Server

I trigger DML sono quelli attivati ​​dai comandi DML (INSERT, UPDATE o DELETE) su tabelle o viste. Possiamo creare tali Trigger su quelle tabelle o viste solo dove risiedono i dati in modo che accettino comandi DML su di essi.

In base al tempo di attivazione/richiamo, i trigger DML possono essere dei seguenti tipi:

  • PER o DOPO Tipo di trigger:il trigger viene richiamato dopo il completamento corretto dell'istruzione DML su una tabella o vista. Nota:è possibile creare il trigger AFTER solo sulle tabelle, non sulle viste.
  • INVECE DI Tipo di trigger:il trigger verrà invocato prima (INVECE DI) lo script DML eseguito sulla tabella o sulla vista.

SQL Server crea due tabelle speciali o logiche denominate INSERTED e AGGIORNATO ogni volta che vengono creati trigger DML in tabelle o viste. Queste tabelle logiche aiutano a identificare le modifiche ai record che si verificano tramite le operazioni INSERT/UPDATE/ DELETE. In questo modo, garantisce che i trigger DML funzionino in modo efficace.

  • INSERITO la tabella logica memorizza le copie dei nuovi record dei record modificati durante le operazioni INSERT e UPDATE. Quando un nuovo record viene aggiunto alla tabella effettiva, viene aggiunto anche alla tabella INSERTED. Allo stesso modo, qualsiasi modifica ai record esistenti tramite l'istruzione UPDATE sposta gli ultimi valori nella tabella INSERTED e i valori precedenti nella tabella logica DELETED.
  • ELIMINATO la tabella logica memorizza le copie dei valori precedenti durante le operazioni UPDATE e DELETE. Ogni volta che un record viene aggiornato, i valori precedenti vengono copiati nella tabella DELETED. Ogni volta che un record viene eliminato dalla tabella effettiva, i record vengono inseriti nella tabella DELETED.

SQL Server dispone di funzioni integrate COLUMN_UPDATED() e UPDATE() per identificare la presenza di operazioni INSERT o UPDATE sulla particolare colonna.

  • COLUMN_UPDATED() restituisce i valori varbinary delle colonne interessate dalle operazioni INSERT o UPDATE.
  • AGGIORNAMENTO() accetta il nome della colonna come parametro di input e restituisce l'informazione se quella colonna ha modifiche ai dati come parte delle operazioni INSERT o UPDATE.

Il NON PER LA REPLICA può essere utilizzata nei trigger DML per evitare di attivarli per le modifiche apportate tramite il processo di replica.

I trigger DML possono essere creati anche con .Net Framework Common Language Runtime (CLR).

Il sistema DMV sys.triggers memorizza l'elenco di tutti i trigger con ambito database. Possiamo utilizzare la query seguente per recuperare i dettagli di tutti i trigger DML all'interno di un database:

SELECT * 
FROM sys.triggers
WHERE type = 'TR';

Le definizioni del trigger DML possono essere visualizzate se il trigger non è crittografato. Usiamo una delle seguenti opzioni:

sys.sql_modules

SELECT OBJECT_SCHEMA_NAME(object_id, db_id()) Schema_name, OBJECT_NAME(object_id) Trigger_Name, definition
FROM sys.sql_modules  
WHERE object_id = OBJECT_ID(<trigger_name>);   

OBJECT_DEFINITION() funzione

SELECT OBJECT_DEFINITION (OBJECT_ID(<trigger_name>)) AS ObjectDefinition; 

sp_helptext procedura memorizzata

EXEC sp_helptext '<trigger_name>';

Tutti i possibili eventi DML sono disponibili in sys.events tavolo. Possiamo visualizzarli utilizzando la query seguente:

SELECT * 
FROM sys.events;

Sintassi del trigger DML

CREATE TRIGGER <trigger_name>
ON <schema_name.table_name | schema_name.view_name > 
[ WITH <DML_trigger_option> [ ,...n ] ]  
{ FOR | AFTER | INSTEAD OF} <event_type>
AS { sql_statement | EXTERNAL NAME <method specifier> }  

A scopo dimostrativo, ho creato due tabelle denominate Vendite e Cronologia vendite con poche colonne nel database di test:

CREATE TABLE Sales (SalesId int IDENTITY NOT NULL, SalesDate datetime, Itemcount int, price money);
CREATE TABLE SalesHistory (SalesId int NOT NULL, SalesDate datetime, Itemcount int, price money, ChangeType varchar(10), ChangeDate datetime DEFAULT GETDATE(), ChangedUser varchar(100) DEFAULT SUSER_NAME());
GO

Come puoi vedere, la Cronologia vendite la tabella ha 3 colonne aggiuntive per tenere traccia della data modificata e del nome utente che ha richiamato la modifica. Se necessario, possiamo avere un'altra colonna Identità definita e rendila anche una chiave primaria.

INSERIRE trigger

Creiamo un semplice trigger INSERT sulle Vendite tabelle per INSERIRE eventuali nuove modifiche ai record in SalesHistory tavolo. Usa lo script seguente:

CREATE TRIGGER TR_INS_Sales ON Sales
FOR INSERT 
AS
BEGIN
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    	,SalesDate
	,Itemcount
	,price
	,'INSERT'
  FROM inserted
END
GO

Per spiegare la sintassi del trigger, abbiamo creato un trigger DML denominato TR_INS_Sales sulle Vendite tavolo. Deve attivare il trigger solo per le operazioni INSERT, inserendo record nella SalesHistory tabella dalla tabella inserita.

Come sappiamo, inserito è una tabella logica che cattura le modifiche che si verificano nella tabella di origine (il Vendite tabella nel nostro caso).

Possiamo vedere un'altra tabella logica speciale eliminata nel trigger UPDATE perché eliminato la tabella non è applicabile per i trigger INSERT.

Aggiungiamo un nuovo record per verificare se i record sono inseriti nella SalesHistory tabella automaticamente.

INSERT INTO Sales(SalesDate,Itemcount,price)
VALUES ('2021-01-01', 5, 100);

Anche se abbiamo inserito un solo record nelle Vendite tabella, otteniamo 2 righe della 1 riga interessata Messaggio. Il secondo record viene visualizzato a causa dell'operazione INSERT come parte del trigger invocato dall'attività INSERT sulle Vendite tabella:inserimento di un record nella Cronologia vendite tabella.

Verifichiamo i record di entrambe le Vendite e Cronologia vendite tabelle:

SELECT * 
FROM Sales

SELECT * 
FROM SalesHistory

Possiamo vedere che ChangeDate e Utente modificato vengono popolati automaticamente. È perché abbiamo progettato la nostra Cronologia tabella con i valori predefiniti come GETDATE() e SUSER_NAME() .

Gli utenti finali possono vedere tramite Trigger o altri mezzi che il loro INSERT è stato controllato tramite la 1 riga aggiuntiva interessata Messaggio. Se desideri monitorare le modifiche senza informare gli utenti, devi applicare SET ROWCOUNT ON comando. Sopprime i risultati visualizzati per le operazioni DML che si verificano all'interno del trigger.

ALTERIAMO il nostro trigger usando lo script con SET ROWCOUNT ON opzione e guardalo in azione:

ALTER TRIGGER TR_INS_Sales ON Sales
FOR INSERT 
AS
BEGIN
SET NOCOUNT ON
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    ,SalesDate
	,Itemcount
	,price
	,'INSERT'
  FROM inserted
END
GO

Ora inseriamo un altro record nelle Vendite tabella:

INSERT INTO Sales(SalesDate,Itemcount,price)
VALUES ('2021-02-01', 1, 50);

Possiamo vedere solo una singola 1 riga interessata Messaggio. Pertanto, il pubblico di destinazione potrebbe non essere informato del fatto che le sue azioni sono sotto monitoraggio.

Verifichiamo se il trigger INSERT è stato richiamato verificando le Vendite e Cronologia vendite tabelle.

Sì, l'evento INSERT sulle Vendite la tabella è stata attivata correttamente. I record sono stati inseriti nella SalesHistory tabella senza avvisare gli utenti.

Pertanto, se crei attivatori a fini di controllo, SET NOCOUNT ON è necessario. Consente l'auditing senza avvisare nessuno.

Trigger AGGIORNAMENTO

Prima di creare un effettivo trigger UPDATE sulle Vendite tabella, facciamo ancora una volta riferimento alle speciali tabelle logiche inserite e cancellate. Crea un trigger UPDATE di esempio su Vendite tabella:

CREATE TRIGGER TR_UPD_Sales ON Sales
FOR UPDATE 
AS
BEGIN
SELECT * FROM inserted
SELECT * FROM deleted
END
GO

Il trigger UPDATE è stato creato correttamente. Ora, INSERISCI un nuovo record in modo errato. Successivamente lo aggiorneremo per verificare il trigger UPDATE in azione:

INSERT INTO Sales(SalesDate,Itemcount,price)
VALUES ('2021-02-01', 1, 50);

Abbiamo i seguenti record nelle Vendite e Cronologia vendite tabella:

Aggiorniamo SalesId =3 nelle Vendite tabella con nuovi valori. Vedremo i dati nelle tabelle inserite ed eliminate:

UPDATE Sales
SET SalesDate = '2021-03-01'
	, Itemcount = 3
	, price = 500
WHERE SalesId = 3

Quando viene eseguita l'operazione di AGGIORNAMENTO, tutti i valori nuovi o modificati saranno disponibili nella tabella inserita e i vecchi valori saranno disponibili nella tabella eliminata:

Ora modifichiamo il trigger UPDATE con lo script seguente e verifichiamolo in azione:

ALTER TRIGGER TR_UPD_Sales ON Sales
FOR UPDATE 
AS
BEGIN
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    ,SalesDate
	,Itemcount
	,price
	,'UPDATE'
  FROM inserted
END
GO

Il trigger UPDATE è stato modificato correttamente e possiamo eseguire nuovamente lo stesso script UPDATE:

Ora possiamo vedere 1 riga interessata messaggio due volte. Indica l'esecuzione dell'operazione di AGGIORNAMENTO sulle Vendite tabella e l'operazione INSERT nella SalesHistory tavolo. Verifichiamolo selezionando in entrambe le tabelle:

L'attività UPDATE è stata monitorata nella Cronologia vendite tabella come nuovo record. Prima di quel record, ne abbiamo un altro che mostra quando il record è stato inserito per primo.

ELIMINA trigger

Finora abbiamo testato il FOR o DOPO tipo di trigger per entrambe le operazioni INSERT o UPDATE. Ora possiamo provare a utilizzare INVECE DI tipo di trigger DML per l'operazione DELETE. Usa lo script seguente:

CREATE TRIGGER TR_DEL_Sales ON Sales
INSTEAD OF DELETE 
AS
BEGIN
	RAISERROR ('Notify Sales Team', 16, 10);  
END
GO

Il trigger DELETE è stato creato correttamente. Invierà un messaggio di errore al client invece di eseguire il comando DELETE su Vendite tabella.

Proviamo a eliminare il record SalesID =3 dalle Vendite tabella utilizzando lo script seguente:

DELETE FROM Sales
WHERE SalesId = 3

Abbiamo impedito agli utenti di eliminare i record dalle Vendite tavolo. Il trigger ha generato un messaggio di errore.

Verifichiamo anche se il record è stato eliminato dalle Vendite tabella e se sono state apportate modifiche alla Cronologia vendite tabella:

Poiché abbiamo eseguito lo script del trigger prima dell'effettiva istruzione DELETE utilizzando INSTEAD OF trigger, l'operazione DELETE su SalesId=3 non ha avuto esito positivo. Pertanto, nessuna modifica è stata applicata a entrambe le Vendite e Cronologia vendite tabella.

Modifichiamo il trigger utilizzando lo script seguente per identificare il tentativo di DELETE sulla tabella:

ALTER TRIGGER TR_DEL_Sales ON Sales
INSTEAD OF DELETE 
AS
BEGIN
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    ,SalesDate
	,Itemcount
	,price
	,'DELETE ATP'
  FROM deleted 
END
GO

Il trigger è stato modificato correttamente. Eliminiamo SalesId =3 record dalle Vendite di nuovo la tabella:

L'esecuzione dell'istruzione DELETE mostra la 1 riga interessata messaggio due volte. Controlliamo i record delle Vendite e Cronologia vendite tabelle per vedere cosa succede esattamente lì:

La logica utilizzata nel trigger DELETE era di acquisire eventuali tentativi di DELETE sulla tabella senza eliminare effettivamente il record dalle Vendite tabella utilizzando INVECE DI grilletto. Possiamo confermare che il record non è stato eliminato dalle Vendite tabella ed è stato inserito un nuovo record nella Cronologia vendite tabella.

Un unico trigger per gestire l'operazione INSERT, UPDATE ed DELETE

Finora abbiamo creato 3 trigger per gestire le operazioni INSERT, UPDATE e DELETE su una singola tabella. Se abbiamo più trigger, sarebbe difficile gestirli, soprattutto se non sono adeguatamente documentati. Possono verificarsi problemi di prestazioni se gli sviluppatori hanno utilizzato una logica contraddittoria su più trigger.

Personalmente consiglio di utilizzare un singolo trigger con tutta la logica combinata per evitare potenziali perdite di dati o problemi di prestazioni. Possiamo provare a combinare 3 trigger in un unico trigger per prestazioni migliori. Ma prima di farlo, esaminiamo come eliminare i trigger esistenti e come disabilitare o abilitare i trigger.

Fai cadere il grilletto

Per unire 3 trigger in uno solo, dobbiamo prima DROP questi 3 trigger. È possibile tramite entrambi gli approcci SSMS e T-SQL.

In SSMS, espandi il Test database > Tabelle > Vendite tabella> Trigger .

Possiamo vedere i nostri 3 trigger creati finora:

Per rilasciare un attivatore, fai semplicemente clic con il pulsante destro del mouse su di esso> Elimina > OK .

Se preferisci utilizzare T-SQL, consulta la sintassi seguente per eliminare il trigger:

DROP TRIGGER <trigger_name>

C'è il TR_INS_Sales attivatore che abbiamo creato su Vendite tavolo. Lo script sarà:

DROP TRIGGER TR_INS_Sales

Importante :l'eliminazione di una tabella elimina tutti i trigger per impostazione predefinita.

Disabilita e abilita trigger

Invece di rilasciare il trigger, possiamo disabilitarlo temporaneamente con Disabilita attivatore opzione tramite SSMS o T-SQL.

In SSMS, fai clic con il pulsante destro del mouse su Nome trigger> Disattiva . Una volta disabilitato, il trigger non verrà attivato finché non lo riattiverai.

Mentre il Trigger funziona, Abilita l'opzione è disattivata. Quando lo disabiliti, Abilita l'opzione diventerà visibile e attiva.

Se preferisci utilizzare T-SQL, puoi disabilitare e abilitare i trigger utilizzando gli script seguenti:

-- To Disable all triggers on a specific table
DISABLE TRIGGER ALL ON <table_name>;

-- To Disable a specific trigger on a table
DISABLE TRIGGER <trigger_name> ON <table_name>;

-- To Enable all triggers on a specific table
ENABLE TRIGGER ALL ON <table_name>;

-- To Enable a specific trigger on a table
ENABLE TRIGGER <trigger_name> ON <table_name>;

Per disabilitare e abilitare il nostro particolare TR_INS_Sales attivare sulle Vendite tabella, utilizziamo gli script seguenti:

-- To Disable TR_INS_Sales trigger on Sales table
DISABLE TRIGGER TR_INS_Sales ON Sales;

-- To Enable TR_INS_Sales trigger on Sales table
ENABLE TRIGGER TR_INS_Sales ON Sales;

Pertanto, abbiamo imparato a DROP , DISATTIVA e ABILITA trigger. Rilascerò 3 trigger esistenti e creerò un unico trigger che copre tutte e 3 le operazioni o l'inserimento, l'aggiornamento e l'eliminazione utilizzando lo script seguente:

DROP TRIGGER TR_INS_Sales
DROP TRIGGER TR_UPD_Sales
DROP TRIGGER TR_DEL_Sales
GO

CREATE TRIGGER TR_INS_UPD_DEL_Sales ON Sales
FOR INSERT, UPDATE, DELETE
AS
BEGIN
IF (SELECT COUNT (*) FROM deleted) = 0
BEGIN
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    ,SalesDate
	,Itemcount
	,price
	,'INSERT'
  FROM inserted
END
ELSE IF (SELECT COUNT (*) FROM inserted) = 0
BEGIN
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    ,SalesDate
	,Itemcount
	,price
	,'DELETE'
  FROM deleted
END
ELSE IF (UPDATE (SalesDate) OR UPDATE (ItemCount) OR UPDATE (Price))
BEGIN
  INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
  SELECT SalesId
    ,SalesDate
	,Itemcount
	,price
	,'UPDATE'
  FROM inserted
END 
END
GO

La creazione del singolo trigger è riuscita. Abbiamo utilizzato la logica per identificare l'operazione utilizzando le tabelle inserite ed eliminate.

Per l'operazione INSERT, la tabella eliminata non verrà popolata. Per l'operazione DELETE, la tabella inserita non verrà popolata. Possiamo identificare facilmente queste operazioni. Se queste 2 condizioni non corrispondono, si tratta di un'operazione UPDATE e possiamo utilizzare una semplice istruzione ELSE.

Ho usato UPDATE() funzione per mostrare come funziona. Se ci fossero aggiornamenti su quelle colonne, l'azione di attivazione UPDATE si attiverebbe. Possiamo anche usare COLUMNS_UPDATED() funzione di cui abbiamo discusso in precedenza per identificare anche un'operazione di AGGIORNAMENTO.

Testiamo il nostro nuovo trigger inserendo un nuovo record:

INSERT INTO Sales(SalesDate,Itemcount,price)
VALUES ('2021-04-01', 4, 400);

Verifica dei record nelle Vendite e Cronologia vendite le tabelle mostrano i dati come di seguito:

Proviamo ad aggiornare SalesId =2 registrare:

UPDATE Sales
SET price = 250
WHERE SalesId = 2;

Proviamo uno script DELETE tramite questa procedura su SalesId =4 registrare:

DELETE FROM Sales
WHERE SalesId = 4;

Come possiamo notare, SalesId =4 è stato eliminato dalle Vendite tabella poiché questo è un FOR o DOPO trigger, facendo in modo che l'operazione DELETE abbia successo sulle Vendite tabella e quindi inserire un record nella Cronologia vendite tabella.

Scopo dei trigger DML

I trigger DML sono efficaci per i seguenti scenari:

  1. Traccia le modifiche storiche delle operazioni INSERT, UPDATE ed DELETE su una tabella specifica.
  2. Controlla gli eventi DML che si verificano su una tabella senza esporre l'attività di controllo agli utenti.
  3. Impedisci che le modifiche DML avvengano su una tabella tramite INVECE DI attiva e avvisa gli utenti con un messaggio di errore specifico.
  4. Invia notifiche a persone mirate al raggiungimento di condizioni predefinite.
  5. Avviare il processo di SQL Server Agent o qualsiasi altro processo ogni volta che si raggiungono condizioni predefinite.

Inoltre, puoi usarli per qualsiasi altro requisito di logica aziendale che puoi implementare con le istruzioni T-SQL.

Conclusione

Il corpo del trigger DML è simile alla stored procedure. Possiamo implementare qualsiasi logica aziendale richiesta, ma dobbiamo fare attenzione mentre scriviamo quella logica coinvolta per evitare potenziali problemi.

Anche se SQL Server supporta la creazione di più trigger su un'unica tabella, è meglio consolidare in un unico trigger. In questo modo, puoi gestire facilmente i trigger e risolverli più velocemente. Ogni volta che vengono implementati attivatori DML per scopi di controllo, assicurati che SET NOCOUNT ON l'opzione viene utilizzata in modo efficace.

Nel prossimo articolo esamineremo i trigger DDL e i trigger di accesso.