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

Trigger di SQL Server – Parte 2 Trigger DDL e LOGON

In SQL Server, i trigger sono oggetti di database che verranno eseguiti ogni volta che si verifica un evento di trigger nel database o nel server. I trigger svolgono un ruolo chiave nel raggiungimento di requisiti aziendali come l'avviso di persone mirate in base a una condizione raggiunta, l'avvio di un lavoro o altre operazioni. Nel precedente articolo sui trigger DML, abbiamo parlato di Trigger, tipi di Trigger e varie opzioni di Trigger disponibili per i Trigger DML. In questo articolo, esploreremo i trigger SQL DDL e LOGON.

Trigger DDL

I trigger DDL possono essere attivati ​​per una varietà di eventi con ambito server o database, inclusi i comandi DDL e DCL. DDL sta per Data Definition Language che viene utilizzato per CREATE, ALTER, DROP qualsiasi oggetto e DCL sta per istruzioni Data Control Language come i comandi GRANT, DENY e REVOKE. Di seguito sono riportate le caratteristiche dei trigger DDL SQL.

  1. I trigger DDL possono essere creati a livello di database o di istanza del server coprendo un'ampia varietà di operazioni DDL o operazioni simili a DDL, ad es. Comandi DCL.
  2. I trigger DDL possono essere invocati o attivati ​​solo come tipo di trigger FOR o AFTER. SQL Server non supporta INSTEAD OF DDL Trigger e possiamo vedere come impedire alcune operazioni DDL tramite DDL Trigger.
  3. SQL Server dispone di funzioni integrate come EVENTDATA() e IS_MEMBER() da utilizzare all'interno dei trigger DDL per ottenere ulteriori informazioni relative agli eventi Trigger.
      La funzione
    1. EVENTDATA() restituisce dettagli completi sugli eventi con ambito database o server in formato XML nell'ambito del trigger DDL con ambito database o server o anche dei trigger di accesso. La funzione EVENTDATA() restituisce i dettagli dell'evento completi per la sessione che esegue le attività DDL o di accesso. EVENTDATA() restituisce i dettagli seguenti
      • EventType – Tipo di evento che attiva il trigger DDL disponibile nella tabella sys.trigger_event_types.
      • PostTime:ora in cui l'evento è stato attivato o pubblicato.
      • SPID – ID sessione dell'evento.
      • ServerName:nome dell'istanza di SQL Server in cui è stato attivato l'evento.
      • LoginName:nome di accesso di SQL Server che ha eseguito l'evento.
      • UserName – Nome utente del Login che sarà dbo per impostazione predefinita.
      • DatabaseName:nome del database con cui è stato attivato il trigger DDL.
      • SchemaName:nome dello schema dell'oggetto interessato.
      • NomeOggetto – Nome oggetto interessato.
      • ObjectType:tipo di oggetto SQL Server come tabella, vista, stored procedure.
      • TSQLCommand – Script T-SQL eseguito da un utente che ha invocato il trigger DDL.
      • SetOptions:le opzioni SET utilizzate dall'utente o dal client come SSMS durante l'esecuzione di TSQLCommand.
      • CommandText:istruzioni DDL o DCL effettive con l'evento DDL specificato nella tabella sys.trigger_event_types.
    2. La funzione
    3. IS_MEMBER() restituisce se l'utente corrente è un membro del gruppo Windows o del ruolo del database di SQL ServerSQL Server o meno.
  4. System DMV sys.triggers archivia l'elenco di tutti i trigger con ambito database. Possiamo utilizzare la query seguente per recuperare i dettagli di tutti i trigger DDL con ambito database.
SELECT * 
FROM sys.triggers
WHERE type = 'TR';
  1. System DMV sys.server_triggers archivia l'elenco di tutti i trigger con ambito Server e possiamo utilizzare la query seguente per recuperare i dettagli su tutti i trigger DDL con ambito Server.
SELECT * 
FROM sys.server_triggers;
  1. Le definizioni di trigger DDL possono essere visualizzate se il trigger non è crittografato utilizzando una delle seguenti opzioni da sys.sql_modules o utilizzando la funzione OBJECT_DEFINITION() o utilizzando la stored procedure sp_helptext.
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>);   

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

EXEC sp_helptext '<trigger_name>';
  1. Tutti i possibili eventi DDL sono disponibili nella tabella sys.trigger_event_types e possono essere visualizzati utilizzando la query seguente.
SELECT *
FROM sys.trigger_event_types;

La sintassi di un trigger DDL è:

CREATE TRIGGER <trigger_name>
ON < ALL SERVER | DATABASE > 
[ WITH <DDL_trigger_option> [ ,...n ] ]  
{ FOR | AFTER } <event_type>
AS { sql_statement | EXTERNAL NAME <method specifier> }  

Creazione di un trigger DDL con ambito database

Creiamo un trigger con ambito database per tenere traccia di tutte le creazioni di tabelle e accedere a una tabella di registrazione denominata Track_DDL_Changes utilizzando lo script seguente.

CREATE TABLE Track_DDL_Changes (EventData xml, PostDtm datetime)
GO
CREATE TRIGGER TR_D_CREATETABLE
ON DATABASE
FOR CREATE_TABLE
AS
BEGIN
	INSERT INTO Track_DDL_Changes
	SELECT EVENTDATA(),GETDATE()
END
GO

Creiamo una nuova tabella denominata trigger_test e verifichiamo se l'evento CREATE TABLE è stato controllato o meno utilizzando lo script seguente.

CREATE TABLE Trigger_Test ( a int, b datetime);

La selezione dei dati dalla tabella Track_DDL_Changes mostra che l'evento CREATE_TABLE sopra è stato acquisito correttamente come mostrato di seguito:

Facendo clic sul valore EventData si aprirà il valore XML EVENTDATA() in una nuova finestra come mostrato di seguito.

Siamo in grado di verificare i dettagli completi sull'evento di attivazione tramite la funzione EVENTDATA() e quindi la funzione EVENTDATA() giocherebbe un ruolo significativo per qualsiasi trigger DDL o LOGON.

Possiamo migliorare ulteriormente il nostro trigger DDL con l'aiuto della funzione EVENTDATA() e dell'analisi XML e impedire a chiunque di creare tabelle nel database di test utilizzando lo script indicato di seguito:

CREATE TRIGGER TR_D_PREVENT_CREATETABLE
ON DATABASE
FOR CREATE_TABLE
AS
BEGIN
   SELECT EVENTDATA().value  
        ('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]','nvarchar(max)')  
   RAISERROR ('Creation of New tables restricted in this database, Kindly contact DBA.', 16, 1)   
   ROLLBACK  
END
GO

La creazione del trigger con ambito database è stata completata correttamente e verifichiamo creando un'altra tabella utilizzando lo script seguente.

CREATE TABLE Trigger_Test1 (a int, b datetime);

Il trigger ci ha impedito di creare nuove tabelle su questo database e ha lasciato un messaggio significativo anche agli utenti. Allo stesso modo, possiamo gestire qualsiasi altro evento con ambito DDL o Server per soddisfare i requisiti.

Per eliminare il trigger DDL con ambito database, è necessario utilizzare la sintassi seguente:

DROP TRIGGER <trigger_name> ON DATABASE;

E per eliminare il trigger che abbiamo creato proprio ora, lo script sarebbe

DROP TRIGGER TR_D_PREVENT_CREATETABLE ON DATABASE;

Per visualizzare i trigger DDL con ambito database in SSMS, espandi il database di test -> Programmabilità -> Trigger del database come mostrato di seguito.

Analogamente ai trigger DML SQL, i trigger DDL possono essere eliminati, disabilitati o abilitati facendo semplicemente clic con il pulsante destro del mouse sul nome del trigger come mostrato di seguito.

Tramite T-SQL, possiamo eliminare, disabilitare o abilitare il trigger DDL con ambito database utilizzando la sintassi seguente:

-- DROP Database scoped DDL Trigger
DROP TRIGGER <trigger_name> ON DATABASE;
-- Enable Database scoped DDL Trigger
ENABLE TRIGGER <trigger_name> ON DATABASE;
-- Disable Database scoped DDL Trigger
DISABLE TRIGGER <trigger_name> ON DATABASE;

Per disabilitare il trigger che abbiamo creato, potrebbe essere necessario utilizzare lo script seguente.

-- DROP Database scoped DDL Trigger
DROP TRIGGER TR_D_PREVENT_CREATETABLE ON DATABASE;
-- Enable Database scoped DDL Trigger
ENABLE TRIGGER TR_D_PREVENT_CREATETABLE ON DATABASE;
-- Disable Database scoped DDL Trigger
DISABLE TRIGGER TR_D_PREVENT_CREATETABLE ON DATABASE;

Creazione di un trigger DDL con ambito server

Il trigger DDL con ambito server segue la stessa sintassi simile al trigger DDL con ambito database, tranne per il fatto che gli eventi saranno basati sull'ambito del server.

Proviamo a creare un trigger DDL con ambito server per impedire a qualsiasi utente di creare un nuovo database su questa istanza del server utilizzando lo script seguente.

CREATE TRIGGER TR_S_PREVENT_CREATEDATABASE
ON ALL SERVER
FOR CREATE_DATABASE
AS
BEGIN
   SELECT EVENTDATA().value  
        ('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]','nvarchar(max)')  
   RAISERROR ('Creation of New Databases restricted in this Instance, Kindly contact DBA.', 16, 1)   
   ROLLBACK  
END
GO

Quando si tenta di creare un nuovo database utilizzando il comando seguente, riceveremo un errore come mostrato di seguito.

CREATE DATABASE DATABASE_TEST;

In SSMS, Trigger DDL con ambito server in Trigger nella sezione Oggetti server, come mostrato di seguito.

Possiamo eliminare, disabilitare o abilitare il trigger DDL con ambito server semplicemente facendo clic con il pulsante destro del mouse sul trigger DDL con ambito server come mostrato di seguito.

Tramite T-SQL, possiamo eliminare o disabilitare o abilitare utilizzando il comando seguente.

-- DROP Server scoped DDL Trigger
DROP TRIGGER TR_S_PREVENT_CREATEDATABASE ON ALL SERVER;
-- Disable Server scoped DDL Trigger
DISABLE TRIGGER TR_S_PREVENT_CREATEDATABASE ON ALL SERVER;
-- Enable Server scoped DDL Trigger
ENABLE TRIGGER TR_S_PREVENT_CREATEDATABASE ON ALL SERVER;

Lo scopo dei trigger DDL

  1. Per controllare tutti gli eventi DDL che si verificano a livello di database o server.
  2. Per impedire che si verifichino eventi DDL a livello di database o server.
  3. Per avvisare ogni volta che si verificano eventi DDL a livello di database o server.

Trigger di accesso

I trigger di accesso indicati dal nome vengono eseguiti per gli eventi di accesso in SQL ServerSQL Server. Una volta completata la fase di autenticazione per un evento di accesso, lo script del trigger di accesso verrà eseguito in aggiunta all'attività di accesso. Se l'accesso non viene autenticato correttamente, i trigger di ACCESSO non verranno attivati. I trigger di accesso verranno elencati in SSMS nella sezione Trigger di Oggetti server. La sintassi di un trigger LOGON è la seguente:

CREATE TRIGGER <schema_name.trigger_name>
ON ALL SERVER
{ FOR| AFTER } LOGON    
AS { sql_statement  [ ; ] [ ,...n ] | EXTERNAL NAME < method specifier >  [ ; ] }  

Crea trigger

Creiamo un semplice trigger LOGON per acquisire maggiori informazioni sull'evento LOGON dalla funzione EVENTDATA() come mostrato di seguito.

CREATE TABLE Track_LOGON_EVENTS (EventData xml, PostDtm datetime)
GO
CREATE TRIGGER TR_LOGON
ON ALL SERVER
FOR LOGON
AS
BEGIN
	INSERT INTO Track_LOGON_EVENTS
	SELECT EVENTDATA(),GETDATE();
END
GO

Il trigger LOGON sopra acquisirà tutti i dettagli su un'attività di accesso simile a quella che abbiamo notato durante l'utilizzo della funzione EVENTDATA() in DDL Trigger. È necessario prestare attenzione durante la pianificazione dell'utilizzo dei trigger LOGON poiché se ci fossero errori logici all'interno del trigger, non consentirebbe a nessuno o alla maggior parte degli utenti di connettersi all'istanza di SQL Server.

Per eliminare, disabilitare o abilitare i trigger di ACCESSO, possiamo utilizzare lo script seguente.

-- DROP LOGON Trigger
DROP TRIGGER TR_LOGON ON ALL SERVER;
-- Disable LOGON Trigger
DISABLE TRIGGER TR_LOGON ON ALL SERVER;
-- Enable LOGON Trigger
ENABLE TRIGGER TR_LOGON ON ALL SERVER;

Lo scopo dei trigger di LOGON

  1. Per controllare tutti gli eventi di accesso che si verificano sul server.
  2. Per impedire che si verifichino eventi di LOGON sul server
  3. Per avvisare ogni volta che si verificano eventi di LOGON sul server.

Proprietà del trigger

sp_settriggerorder

sp_settriggerorder viene utilizzato per definire l'ordine di esecuzione del trigger solo per il primo e l'ultimo trigger. Se ci sono più di 2 trigger DML in una tabella, diciamo 5 trigger DML, allora possiamo definire il primo trigger DML e l'ultimo trigger DML ma non possiamo definire l'ordine dei 3 trigger centrali.

Nota: L'impostazione dell'opzione FIRST o LAST è specifica per una particolare categoria di eventi per i trigger DML. Ad esempio, in una tabella con 3 trigger INSERT, possiamo definire quale trigger INSERT è PRIMO e quale trigger INSERT è ULTIMO. Se hai 3 trigger su una tabella come INSERT, UPDATE e DELETE, non è necessario impostare la condizione dell'ordine Trigger.

La sintassi per impostare l'ordine di attivazione sarebbe la seguente:

exec sp_settriggerorder @triggername = '<trigger_schema_name.trigger_name>' 
    , @order = 'FIRST' | 'LAST'   
    , @stmttype = '<trigger event type>'   
    , @namespace = 'DATABASE' | 'SERVER' | 'NULL'

Per i trigger DDL, è possibile definire il primo e l'ultimo trigger con ambito server e quindi definire il primo e l'ultimo trigger con ambito database. Ad esempio, se abbiamo 5 trigger con ambito server e 5 trigger con ambito database, l'ordine può essere definito in questo modo:

  1. Primo trigger per trigger DDL con ambito server
  2. 3 altri trigger DDL con ambito server in ordine casuale
  3. Ultimo trigger per trigger DDL con ambito server.
  4. Primo trigger per trigger DDL con ambito database (uno per database)
  5. 3 Altri trigger DDL con ambito database in ordine casuale
  6. Ultimo trigger per trigger DDL con ambito database.

Per quanto riguarda l'impostazione della prima o dell'ultima opzione, i trigger DDL con ambito database possono essere ordinati all'interno del database e i trigger DDL con ambito server a livello di istanza.

Anche se SQL Server ci consente di creare molti trigger su una tabella, si consiglia di analizzare attentamente i requisiti del trigger per una migliore manutenzione e risoluzione dei problemi.

Trigger ricorsivi

SQL Server supporta anche la chiamata ricorsiva di trigger per i trigger DML. I trigger ricorsivi possono essere classificati come diretti o indiretti come mostrato di seguito.

Trigger ricorsivi diretti – L'utente o l'applicazione aggiorna un record nella tabella A. Il trigger UPDATE A nella tabella A viene attivato e aggiorna nuovamente la tabella A. Poiché il record nella tabella A è stato aggiornato tramite Trigger, invocherà nuovamente UPDATE Trigger A e ciò avverrà in modo ricorsivo.

Creiamo un trigger ricorsivo diretto nella tabella Sales utilizzando lo script seguente:

CREATE TRIGGER TR_UPD_Recursive_Sales ON Sales
FOR UPDATE 
AS
BEGIN
  UPDATE Sales 
  SET SalesDate = GETDATE() 
  WHERE SalesId = (SELECT SalesId FROM Inserted)
END
GO

Esegui lo script seguente:

UPDATE Sales 
SET SalesDate = GETDATE() 
WHERE SalesId = 3;

Trigger ricorsivi indiretti – L'utente o l'applicazione aggiorna un record nella tabella A. Il trigger UPDATE A nella tabella A viene attivato e aggiorna un record nella tabella B. Se la tabella B dispone di un trigger UPDATE per aggiornare i record alla tabella A, invocherà il trigger UPDATE in Tabella A che accadrà in modo ricorsivo.

Creiamo un trigger ricorsivo indiretto sulle tabelle IDR_Test1 e IDR_Test2 utilizzando lo script seguente:

DROP TABLE IDR_Test1
DROP TABLE IDR_Test2

CREATE TABLE IDR_Test1 (PK int NOT NULL);
GO
INSERT INTO IDR_Test1 
values (10),(20)
GO
CREATE TABLE IDR_Test2 (PK int NOT NULL);
GO
INSERT INTO IDR_Test2
values (10),(20)
GO

CREATE TRIGGER TR_IDR_Test1
ON IDR_Test1
FOR UPDATE 
AS
BEGIN
	UPDATE IDR_Test2
	SET PK = 30
	WHERE PK IN (SELECT PK FROM inserted);
END
GO
 
CREATE TRIGGER TR_Temp2
ON IDR_Test2
FOR UPDATE 
AS
BEGIN
	UPDATE IDR_Test1
	SET PK = 30
	WHERE PK IN (SELECT PK FROM inserted);
END
GO

Esegui lo script seguente:

UPDATE IDR_Test1
SET PK = 1
WHERE PK = 10;

Per evitare questo tipo di chiamata di trigger ricorsivi a livello di database, SQL Server dispone di un'opzione denominata RECURSIVE_TRIGGERS a ogni livello di database per interrompere l'attivazione del trigger ricorsivo. Per impostazione predefinita, l'opzione trigger ricorsivo è impostata su False per un database. Abilita solo se necessario dopo un'attenta considerazione degli impatti sulle prestazioni o delle modifiche ai dati coinvolte.

In SSMS, fai clic con il pulsante destro del mouse sul nostro database di test -> Scegli Proprietà -> Fare clic su Opzioni e scorri verso il basso per vedere che l'opzione Trigger ricorsivi è abilitata o meno come mostrato di seguito. Per Test database, è impostato su False poiché False è il valore predefinito per l'opzione Trigger ricorsivi. Per attivare l'opzione Trigger ricorsivi per un database specifico, fai clic sul valore del menu a discesa, cambialo in True e fai clic su OK.

Tramite T-SQL, possiamo verificare l'opzione Trigger ricorsivo del database di test controllando la colonna is_recursive_triggers_on da sys.databases DMV come mostrato di seguito.

select name, is_recursive_triggers_on
from sys.databases
where name = 'test'

Per modificare l'opzione Trigger ricorsivi per un database (Test nel mio esempio), possiamo eseguire lo script seguente.

ALTER DATABASE [Test] SET RECURSIVE_TRIGGERS ON WITH NO_WAIT
GO

Per disabilitarlo di nuovo allo stato falso (stato predefinito) per un database (Test nel mio esempio), esegui lo script seguente.

ALTER DATABASE [Test] SET RECURSIVE_TRIGGERS OFF WITH NO_WAIT
GO

Trigger nidificati

I trigger ricorsivi sono un classico esempio di trigger nidificati, ma possono esserci pochi altri casi che determinano l'annidamento di più trigger. SQL Server consente la nidificazione dei trigger fino a un massimo di 32 livelli e qualsiasi trigger che supera tale livello di nidificazione verrà annullato da SQL Server. SQL Server dispone di una configurazione a livello di istanza per disabilitare l'opzione Trigger nidificati. Si noti che l'annidamento dei trigger di SQL Server mediante codice CLR o codice gestito non rientra nel limite di 32 livelli in quanto non rientra nell'ambito di SQL Server. Per impostazione predefinita, l'opzione dei trigger nidificati sarà abilitata in tutte le istanze di SQL Server e possiamo disabilitarla come richiesto.

Possiamo verificare se l'opzione dei trigger nidificati è abilitata a livello di istanza in SSMS seguendo i passaggi seguenti:

Fare clic con il pulsante destro del mouse su Server -> Scegli Proprietà -> Fai clic su Avanzate

Per disabilitare o disattivare l'opzione attivatori nidificati, fai clic sul menu a discesa e cambialo in Falso, quindi fai clic su OK .

Tramite T-SQL, possiamo verificare se l'opzione Trigger nidificati è abilitata controllando la colonna value_in_use in sys.configurations DMV per il nome di configurazione dei trigger nidificati.

Per disabilitare questa opzione, è necessario utilizzare sp_configure la procedura memorizzata di sistema come mostrato di seguito:

EXEC sp_configure 'nested triggers', 0;  
GO  
RECONFIGURE;  
GO  

All'interno di qualsiasi trigger DML o DDL, per trovare il livello corrente di nidificazione, SQL Server dispone di una funzione incorporata denominata TRIGGER_NESTLEVEL per restituire il numero di trigger eseguiti per l'istruzione corrente che ha attivato il trigger, incluso se stesso. La sintassi della funzione TRIGGER_NESTLEVEL sarebbe:

SELECT TRIGGER_NESTLEVEL ( object_id, <trigger_type> , <trigger_event_category> )

Dove object_id è l'ID oggetto del trigger, trigger_type sarà AFTER per AFTER trigger e IOT per INSTEAD OF trigger e trigger_event_category sarà DML o DDL.

Ad esempio, se dobbiamo consentire solo il livello di annidamento fino a 10 e aumentare l'errore dopo 10 livelli, possiamo farlo su trigger di test come qui:

IF ((SELECT TRIGGER_NESTLEVEL(OBJECT_ID('test_trigger'), 'AFTER’, 'DML’)) > 10)  
   RAISERROR ('Trigger test_trigger nested more than 10 levels.',16, -1)   

CRIPTATURA

Per crittografare la logica o la definizione del trigger, è possibile utilizzare l'opzione WITH ENCRYPTION nella definizione del trigger in modo simile a tutti gli altri oggetti di SQL Server.

Esegui come clausola

Per eseguire il trigger utilizzando un contesto di sicurezza specifico, è possibile utilizzare la clausola EXECUTE AS nella definizione del trigger.

NON PER REPLICA

Per identificare che il trigger DML non deve essere richiamato durante l'esecuzione tramite modifiche alla replica, la proprietà NOT FOR REPLICATION verrà impostata per tutti gli oggetti nel database dell'iscritto.

Conclusione

Grazie per aver esaminato il ricco articolo sui trigger DDL e sui trigger di accesso in cui abbiamo compreso lo scopo dei trigger DDL e di accesso, come creare o eliminare, disabilitare o abilitare questi trigger insieme a come utilizzare la funzione EVENTDATA() per monitoraggio delle attività DDL o di accesso. Inoltre, abbiamo imparato come impostare in dettaglio l'ordine di esecuzione di più trigger SQL DML o DDL insieme a trigger ricorsivi e nidificati e come gestire con attenzione i trigger ricorsivi o nidificati.