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

Déclencheurs SQL Server – Partie 2 Déclencheurs DDL et LOGON

Dans SQL Server, les déclencheurs sont des objets de base de données qui seront exécutés chaque fois qu'un événement déclencheur se produit sur la base de données ou le serveur. Les déclencheurs jouent un rôle clé dans la réalisation des exigences de l'entreprise, comme alerter les personnes ciblées en fonction d'une condition atteinte, du démarrage d'un travail ou d'autres opérations. Dans l'article précédent sur les déclencheurs DML, nous avons parlé des déclencheurs, des types de déclencheurs et des diverses options de déclencheur disponibles pour les déclencheurs DML. Dans cet article, nous allons explorer les déclencheurs SQL DDL et LOGON.

Déclencheurs DDL

Les déclencheurs DDL peuvent être déclenchés pour une variété d'événements de serveur ou de base de données, y compris les commandes DDL et DCL. DDL signifie Data Definition Language qui est utilisé pour CREATE, ALTER, DROP tous les objets et DCL signifie les instructions Data Control Language comme les commandes GRANT, DENY et REVOKE. Vous trouverez ci-dessous les caractéristiques des déclencheurs SQL DDL.

  1. Les déclencheurs DDL peuvent être créés au niveau de la base de données ou de l'instance du serveur, couvrant une grande variété d'opérations DDL ou d'opérations similaires à DDL, par ex. Commandes DCL.
  2. Les déclencheurs DDL ne peuvent être invoqués ou déclenchés qu'en tant que type de déclencheur FOR ou AFTER. SQL Server ne prend pas en charge INSTEAD OF DDL Trigger et nous pouvons voir comment empêcher certaines opérations DDL via DDL Triggers.
  3. SQL Server dispose de fonctions intégrées telles que EVENTDATA() et IS_MEMBER() à utiliser dans les déclencheurs DDL pour obtenir plus d'informations sur les événements déclencheurs.
    1. La fonction EVENTDATA() renvoie des détails complets sur les événements liés à la base de données ou au serveur au format XML dans le cadre du déclencheur DDL ou des déclencheurs de connexion à la base de données ou au serveur. La fonction EVENTDATA() renvoie les détails complets de l'événement pour la session qui exécute les activités DDL ou de connexion. EVENTDATA() renvoie les détails ci-dessous
      • EventType :type d'événement qui déclenche le déclencheur DDL disponible dans la table sys.trigger_event_types.
      • PostTime :heure à laquelle l'événement a été déclenché ou publié.
      • SPID :ID de session de l'événement.
      • ServerName :nom de l'instance SQL Server dans laquelle l'événement a été déclenché.
      • LoginName - Nom de connexion SQL Server qui a exécuté l'événement.
      • UserName - Nom d'utilisateur de la connexion qui sera dbo par défaut.
      • DatabaseName – Nom de la base de données sous laquelle le déclencheur DDL a été déclenché.
      • SchemaName – Nom du schéma de l'objet qui a été impacté.
      • ObjectName - Nom de l'objet qui a été impacté.
      • ObjectType :type d'objet SQL Server, tel qu'une table, une vue, une procédure stockée.
      • TSQLCommand – Script T-SQL qui a été exécuté par un utilisateur qui a appelé le déclencheur DDL.
      • SetOptions - Options SET utilisées par l'utilisateur ou le client comme SSMS pendant l'exécution de TSQLCommand.
      • CommandText – Instructions DDL ou DCL réelles avec l'événement DDL spécifié dans la table sys.trigger_event_types.
    2. La fonction IS_MEMBER() indique si l'utilisateur actuel est membre du groupe Windows ou du rôle de base de données SQL Server ou non.
  4. System DMV sys.triggers stocke la liste de tous les déclencheurs étendus à la base de données. Nous pouvons utiliser la requête ci-dessous pour récupérer les détails de tous les déclencheurs DDL étendus à la base de données.
SELECT * 
FROM sys.triggers
WHERE type = 'TR';
  1. System DMV sys.server_triggers stocke la liste de tous les déclencheurs de portée serveur et nous pouvons utiliser la requête ci-dessous pour récupérer les détails de tous les déclencheurs DDL de portée serveur.
SELECT * 
FROM sys.server_triggers;
  1. Les définitions de déclencheur DDL peuvent être affichées si le déclencheur n'est pas chiffré en utilisant l'une des options ci-dessous de sys.sql_modules ou en utilisant la fonction OBJECT_DEFINITION() ou en utilisant la procédure stockée 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. Tous les événements DDL possibles sont disponibles dans la table sys.trigger_event_types et peuvent être visualisés à l'aide de la requête ci-dessous.
SELECT *
FROM sys.trigger_event_types;

La syntaxe d'un déclencheur DDL est :

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

Création d'un déclencheur DDL étendu à la base de données

Créons un déclencheur de portée de base de données pour suivre toutes les créations de table et connectez-vous à une table de journalisation nommée Track_DDL_Changes à l'aide du script ci-dessous.

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

Créons une nouvelle table nommée trigger_test et vérifions si l'événement CREATE TABLE a été audité ou non en utilisant le script ci-dessous.

CREATE TABLE Trigger_Test ( a int, b datetime);

La sélection des données dans la table Track_DDL_Changes montre que l'événement CREATE_TABLE ci-dessus a été capturé avec succès, comme indiqué ci-dessous :

Cliquer sur la valeur EventData ouvrira la valeur XML EVENTDATA() dans une nouvelle fenêtre comme indiqué ci-dessous.

Nous sommes en mesure de vérifier tous les détails de l'événement déclencheur via la fonction EVENTDATA() et, par conséquent, la fonction EVENTDATA() jouerait un rôle important pour tout déclencheur DDL ou LOGON.

Nous pouvons encore améliorer notre déclencheur DDL à l'aide de la fonction EVENTDATA() et de l'analyse XML et empêcher quiconque de créer une table dans la base de données de test à l'aide du script ci-dessous :

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 création du déclencheur de portée de base de données s'est terminée avec succès et vérifions en créant une autre table à l'aide du script ci-dessous.

CREATE TABLE Trigger_Test1 (a int, b datetime);

Le déclencheur nous a empêché de créer de nouvelles tables sur cette base de données et a également laissé un message significatif aux utilisateurs. De la même manière, nous pouvons gérer tout autre événement DDL ou de portée serveur pour répondre aux exigences.

Pour supprimer le déclencheur DDL étendu à la base de données, nous devons utiliser la syntaxe ci-dessous :

DROP TRIGGER <trigger_name> ON DATABASE;

Et pour supprimer le déclencheur que nous venons de créer, le script serait

DROP TRIGGER TR_D_PREVENT_CREATETABLE ON DATABASE;

Pour afficher les déclencheurs DDL de portée de base de données dans SSMS, développez la base de données de test -> Programmabilité -> Déclencheurs de base de données comme indiqué ci-dessous.

Semblables aux déclencheurs SQL DML, les déclencheurs DDL peuvent être supprimés, désactivés ou activés en cliquant simplement avec le bouton droit sur le nom du déclencheur, comme indiqué ci-dessous.

Via T-SQL, nous pouvons supprimer ou désactiver ou activer le déclencheur DDL étendu à la base de données en utilisant la syntaxe ci-dessous :

-- 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;

Pour désactiver le déclencheur que nous avons créé, nous devrons peut-être utiliser le script ci-dessous.

-- 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;

Création d'un déclencheur DDL étendu au serveur

Le déclencheur DDL de portée de serveur suit la même syntaxe similaire au déclencheur DDL de portée de base de données, sauf que les événements seront basés sur la portée du serveur.

Essayons de créer un déclencheur DDL étendu au serveur pour empêcher tout utilisateur de créer une nouvelle base de données sur cette instance de serveur à l'aide du script ci-dessous.

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

Lorsque vous essayez de créer une nouvelle base de données à l'aide de la commande ci-dessous, nous recevons une erreur comme indiqué ci-dessous.

CREATE DATABASE DATABASE_TEST;

Dans SSMS, les déclencheurs DDL de portée serveur sous Déclencheurs dans la section Objets serveur, comme indiqué ci-dessous.

Nous pouvons supprimer, désactiver ou activer le déclencheur DDL à portée de serveur en cliquant simplement avec le bouton droit sur le déclencheur DDL à portée de serveur, comme indiqué ci-dessous.

Via T-SQL, nous pouvons supprimer ou désactiver ou activer à l'aide de la commande ci-dessous.

-- 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;

Le but des déclencheurs DDL

  1. Pour auditer tous les événements DDL se produisant au niveau de la base de données ou du serveur.
  2. Pour empêcher tout événement DDL de se produire au niveau de la base de données ou du serveur.
  3. Pour alerter chaque fois qu'un événement DDL se produit au niveau de la base de données ou du serveur.

Déclencheurs de connexion

Les déclencheurs de connexion, comme leur nom l'indique, sont exécutés pour les événements de connexion dans SQL Server. Une fois la phase d'authentification terminée pour un événement de connexion, le script LOGON Trigger serait exécuté en plus de l'activité de connexion. Si la connexion n'est pas authentifiée avec succès, les déclencheurs LOGON ne seront pas déclenchés. Les déclencheurs de connexion seront répertoriés dans SSMS sous la section Déclencheurs des objets serveur. La syntaxe d'un déclencheur de connexion est la suivante :

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

Créer des déclencheurs

Créons un déclencheur LOGON simple pour capturer plus d'informations sur l'événement LOGON à partir de la fonction EVENTDATA() comme indiqué ci-dessous.

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

Le déclencheur LOGON ci-dessus capturera tous les détails d'une activité de connexion similaire à ce que nous avons remarqué lors de l'utilisation de la fonction EVENTDATA() dans DDL Trigger. Nous devons être prudents lors de la planification de l'utilisation des déclencheurs LOGON car s'il y a des erreurs de logique dans le déclencheur, cela ne permettrait à personne ou à la plupart des utilisateurs de se connecter à l'instance de SQL Server.

Pour abandonner, désactiver ou activer les déclencheurs de connexion, nous pouvons utiliser le script ci-dessous.

-- 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;

L'objectif des déclencheurs de connexion

  1. Pour auditer tous les événements de connexion se produisant sur le serveur.
  2. Pour empêcher tout événement LOGON de se produire sur le serveur
  3. Pour alerter chaque fois qu'un événement LOGON se produit sur le serveur.

Propriétés du déclencheur

sp_setriggerorder

sp_setriggerorder est utilisé pour définir l'ordre d'exécution du déclencheur uniquement pour le premier et le dernier déclencheur. S'il y a plus de 2 déclencheurs DML dans une table, disons 5 déclencheurs DML, nous pouvons alors définir le premier déclencheur DML et le dernier déclencheur DML, mais nous ne pouvons pas définir l'ordre des 3 déclencheurs du milieu.

Remarque : La définition de l'option FIRST ou LAST est spécifique à une catégorie d'événement particulière pour les déclencheurs DML. Par exemple, dans une table ayant 3 déclencheurs INSERT, nous pouvons définir quel déclencheur INSERT est PREMIER et quel déclencheur INSERT est DERNIER. Si vous avez 3 déclencheurs sur une table tels que INSERT, UPDATE et DELETE, il n'est pas nécessaire de définir la condition d'ordre des déclencheurs.

La syntaxe pour définir l'ordre des déclencheurs serait la suivante :

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

Pour les déclencheurs DDL, nous pouvons définir les premier et dernier déclencheurs de portée de serveur, puis définir les premier et dernier déclencheurs de portée de base de données. Par exemple, si nous avons 5 déclencheurs de portée de serveur et 5 déclencheurs de portée de base de données, l'ordre peut être défini comme ceci :

  1. Premier déclencheur pour le déclencheur DDL à portée de serveur
  2. 3 autres déclencheurs DDL de portée de serveur dans un ordre aléatoire
  3. Dernier déclencheur pour le déclencheur DDL à portée de serveur.
  4. Premier déclencheur pour le déclencheur DDL de portée de base de données (un par base de données)
  5. 3 autres déclencheurs DDL de portée de base de données dans un ordre aléatoire
  6. Dernier déclencheur pour le déclencheur DDL dans la portée de la base de données.

En ce qui concerne la définition de la première ou de la dernière option, les déclencheurs DDL étendus à la base de données peuvent être classés dans la base de données et les déclencheurs DDL étendus au serveur au niveau de l'instance.

Même si SQL Server nous permet de créer de nombreux déclencheurs sur une table, il est recommandé d'analyser attentivement les exigences du déclencheur pour une meilleure maintenance et un meilleur dépannage.

Déclencheurs récursifs

SQL Server prend également en charge l'appel récursif des déclencheurs pour les déclencheurs DML. Les déclencheurs récursifs peuvent être classés comme directs ou indirects, comme indiqué ci-dessous.

Déclencheurs récursifs directs – L'utilisateur ou l'application met à jour un enregistrement dans la table A. UPDATE Le déclencheur A de la table A est déclenché et met à nouveau à jour la table A. Étant donné que l'enregistrement de la table A a été mis à jour via le déclencheur, il invoquera à nouveau UPDATE Trigger A et cela se produira de manière récursive.

Créons un déclencheur récursif direct sur la table Sales en utilisant le script ci-dessous :

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

Exécutez le script ci-dessous :

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

Déclencheurs récursifs indirects – L'utilisateur ou l'application met à jour un enregistrement dans la table A. Le déclencheur UPDATE A dans la table A est déclenché et met à jour un enregistrement dans la table B. Si la table B a un déclencheur UPDATE pour mettre à jour les enregistrements dans la table A, il appellera le déclencheur UPDATE dans Table A qui se produira de manière récursive.

Créons un déclencheur récursif indirect sur les tables IDR_Test1 et IDR_Test2 à l'aide du script ci-dessous :

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

Exécutez le script ci-dessous :

UPDATE IDR_Test1
SET PK = 1
WHERE PK = 10;

Pour éviter ces types d'appels de déclencheurs récursifs au niveau de la base de données, SQL Server dispose d'une option appelée RECURSIVE_TRIGGERS à chaque niveau de base de données pour interrompre le déclenchement du déclencheur récursif. Par défaut, l'option Déclencheur récursif est définie sur Faux pour une base de données. Activez uniquement si nécessaire après un examen attentif des impacts sur les performances ou des modifications de données impliquées.

Dans SSMS, faites un clic droit sur notre base de données de test -> Choisissez Propriétés -> Cliquez sur Options et faites défiler vers le bas pour voir si l'option Déclencheurs récursifs est activée ou non, comme indiqué ci-dessous. Pour la base de données de test, elle est définie sur False car False est la valeur par défaut de l'option Recursive Triggers. Pour activer l'option Déclencheurs récursifs pour une base de données spécifique, cliquez simplement sur la valeur de la liste déroulante, changez-la en Vrai et cliquez sur OK.

Via T-SQL, nous pouvons vérifier l'option Recursive Trigger de la base de données de test en vérifiant la colonne is_recursive_triggers_on de sys.databases DMV comme indiqué ci-dessous.

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

Pour modifier l'option Déclencheurs récursifs pour une base de données (Test dans mon exemple), nous pouvons exécuter le script ci-dessous.

ALTER DATABASE [Test] SET RECURSIVE_TRIGGERS ON WITH NO_WAIT
GO

Pour le désactiver à l'état faux (état par défaut) pour une base de données (Test dans mon exemple), exécutez le script ci-dessous.

ALTER DATABASE [Test] SET RECURSIVE_TRIGGERS OFF WITH NO_WAIT
GO

Déclencheurs imbriqués

Les déclencheurs récursifs sont un exemple classique de déclencheurs imbriqués, mais il peut y avoir peu d'autres cas entraînant l'imbrication de plusieurs déclencheurs. SQL Server autorise l'imbrication de déclencheurs jusqu'à un maximum de 32 niveaux et tout déclencheur dépassant ce niveau d'imbrication sera annulé par SQL Server. SQL Server dispose d'une configuration à l'échelle de l'instance pour désactiver l'option Déclencheurs imbriqués. Veuillez noter que l'imbrication de déclencheurs SQL Server à l'aide de code CLR ou de code géré ne relève pas de la limite de 32 niveaux, car elle n'entre pas dans le champ d'application de SQL Server. Par défaut, l'option de déclencheurs imbriqués sera activée sur toutes les instances de SQL Server et nous pouvons la désactiver si nécessaire.

Nous pouvons vérifier si l'option de déclencheurs imbriqués est activée au niveau de l'instance dans SSMS en suivant les étapes ci-dessous :

Cliquez avec le bouton droit sur Serveur -> Choisissez Propriétés -> Cliquez sur Avancé

Pour désactiver ou désactiver l'option de déclencheurs imbriqués, cliquez sur le menu déroulant et changez-le en Faux, puis cliquez sur OK .

Via T-SQL, nous pouvons vérifier si l'option Déclencheurs imbriqués est activée en vérifiant la colonne value_in_use dans sys.configurations DMV pour le nom de configuration des déclencheurs imbriqués.

Pour désactiver cette option, nous devons utiliser la procédure stockée système sp_configure comme indiqué ci-dessous :

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

Dans tous les déclencheurs DML ou DDL, pour trouver le niveau actuel d'imbrication, SQL Server dispose d'une fonction intégrée nommée TRIGGER_NESTLEVEL pour renvoyer le nombre de déclencheurs exécutés pour l'instruction actuelle qui a déclenché le déclencheur, y compris lui-même. La syntaxe de la fonction TRIGGER_NESTLEVEL serait :

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

Où object_id est l'identifiant d'objet du déclencheur, trigger_type sera AFTER pour le déclencheur AFTER et IOT pour le déclencheur INSTEAD OF et trigger_event_category sera DML ou DDL.

Par exemple, si nous devons autoriser uniquement le niveau d'imbrication jusqu'à 10 et augmenter l'erreur après 10 niveaux, nous pouvons le faire sur le déclencheur de test comme ici :

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

CHIFFRAGE

Pour chiffrer la logique ou la définition du déclencheur, l'option WITH ENCRYPTION peut être utilisée dans une définition de déclencheur similaire à tous les autres objets SQL Server.

Clause EXECUTE AS

Pour exécuter le déclencheur à l'aide d'un contexte de sécurité spécifique, la clause EXECUTE AS peut être utilisée dans la définition du déclencheur.

PAS POUR LA RÉPLICATION

Pour identifier que le déclencheur DML ne doit pas être appelé lors de l'exécution via des modifications de réplication, la propriété NOT FOR REPLICATION sera définie pour tous les objets de la base de données de l'abonné.

Conclusion

Merci d'avoir parcouru l'article puissant sur les déclencheurs DDL et les déclencheurs de connexion où nous avons compris le but des déclencheurs DDL et de connexion, comment créer ou supprimer, désactiver ou activer ces déclencheurs ainsi que comment utiliser la fonction EVENTDATA() pour suivi des activités DDL ou de connexion. En plus de cela, nous avons appris à définir en détail l'ordre d'exécution de plusieurs déclencheurs SQL DML ou DDL ainsi que des déclencheurs récursifs et imbriqués et à gérer avec précaution les déclencheurs récursifs ou imbriqués.