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

Déclencheurs SQL Server :déclencheurs DML

Dans SQL Server, les déclencheurs sont des objets de base de données qui sont 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 commerciales telles que l'alerte des personnes ciblées, le démarrage d'un travail ou d'autres opérations. Étant donné que les déclencheurs peuvent gérer de nombreuses opérations de ce type, nous devons les définir avec soin pour éviter les impacts sur les performances.

Dans cet article, nous examinerons les déclencheurs, les types de déclencheurs et les différentes options de déclencheur disponibles. Nous explorerons également les précautions nécessaires lors de l'utilisation des déclencheurs DML.

Déclencheurs en SQL

Un déclencheur est un type spécial de procédure stockée qui est exécutée sur des événements définis, exécutant le script défini dans le corps du déclencheur. Il existe plusieurs types de déclencheurs :

  • Déclencheurs DML – pour effectuer des opérations DML telles que les commandes INSERT, UPDATE et DELETE sur les tables.
  • Déclencheurs DDL – pour effectuer des opérations DDL telles que les commandes CREATE, ALTER et DROP sur tous les objets de la base de données ou du serveur.
  • Déclencheurs de connexion – pour la tentative de connexion à une instance de SQL Server lors de l'événement LOGON.

Déclencheurs DML dans SQL Server

Les déclencheurs DML sont ceux déclenchés par les commandes DML (INSERT, UPDATE ou DELETE) sur des tables ou des vues. Nous pouvons créer de tels déclencheurs sur ces tables ou vues uniquement là où résident les données afin qu'elles acceptent les commandes DML sur celles-ci.

En fonction de l'heure de déclenchement/d'appel, les déclencheurs DML peuvent être des types suivants :

  • POUR ou APRÈS Type de déclencheur - le déclencheur est appelé après l'exécution réussie de l'instruction DML sur une table ou une vue. Remarque :il est possible de créer le déclencheur AFTER uniquement sur les tables, pas sur les vues.
  • AU LIEU DE Type de déclencheur - Le déclencheur sera appelé avant (AU LIEU DE) le script DML exécuté sur la table ou la vue.

SQL Server crée deux tables spéciales ou logiques nommées INSERTED et MIS À JOUR chaque fois que des déclencheurs DML sont créés dans des tables ou des vues. Ces tables logiques aident à identifier les modifications d'enregistrement qui se produisent via les opérations INSERT/UPDATE/ DELETE. De cette façon, il garantit que les déclencheurs DML fonctionnent efficacement.

  • INSÉRÉ La table logique stocke des copies des nouveaux enregistrements des enregistrements modifiés lors des opérations INSERT et UPDATE. Lorsqu'un nouvel enregistrement est ajouté à la table réelle, il est également ajouté à la table INSERTED. De même, toute modification apportée aux enregistrements existants via l'instruction UPDATE déplace les dernières valeurs vers la table INSERTED et les valeurs plus anciennes vers la table logique DELETED.
  • SUPPRIMÉ La table logique stocke des copies des anciennes valeurs lors des opérations UPDATE et DELETE. Chaque fois qu'un enregistrement est mis à jour, les anciennes valeurs sont copiées dans la table DELETED. Chaque fois qu'un enregistrement est supprimé de la table réelle, les enregistrements sont insérés dans la table DELETED.

SQL Server a des fonctions intégrées COLUMN_UPDATED() et MISE À JOUR() pour identifier la présence d'opérations INSERT ou UPDATE sur la colonne particulière.

  • COLUMN_UPDATED() renvoie les valeurs varbinary des colonnes qui ont été affectées par les opérations INSERT ou UPDATE.
  • MISE À JOUR() accepte le nom de la colonne comme paramètre d'entrée et renvoie les informations indiquant si cette colonne a des modifications de données dans le cadre des opérations INSERT ou UPDATE.

Le PAS POUR LA RÉPLICATION La propriété peut être utilisée dans les déclencheurs DML pour éviter de les déclencher pour les modifications provenant du processus de réplication.

Les déclencheurs DML peuvent également être créés avec .Net Framework Common Language Runtime (CLR).

Le système DMV sys.triggers stocke la liste de tous les déclencheurs de portée de base de données. Nous pouvons utiliser la requête ci-dessous pour récupérer les détails de tous les déclencheurs DML dans une base de données :

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

Les définitions de déclencheur DML peuvent être consultées si le déclencheur n'est pas chiffré. Nous utilisons l'une des options ci-dessous :

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() fonction

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

sp_helptext procédure stockée

EXEC sp_helptext '<trigger_name>';

Tous les événements DML possibles sont disponibles dans sys.events table. Nous pouvons les afficher à l'aide de la requête ci-dessous :

SELECT * 
FROM sys.events;

Syntaxe du déclencheur 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> }  

À des fins de démonstration, j'ai créé deux tables nommées Sales et Historique des ventes avec peu de colonnes dans la base de données de 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

Comme vous pouvez le voir, l'SalesHistory La table a 3 colonnes supplémentaires pour suivre la date de modification et le nom d'utilisateur qui a invoqué le changement. Si nécessaire, nous pouvons ajouter une colonne d'identité défini et faites-en également une clé primaire.

INSÉRER le déclencheur

Nous créons un déclencheur INSERT simple sur le Ventes tables pour INSÉRER toute nouvelle modification d'enregistrement dans SalesHistory table. Utilisez le script ci-dessous :

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

Pour expliquer la syntaxe du déclencheur, nous avons créé un déclencheur DML nommé TR_INS_Sales sur les Soldes table. Il doit déclencher le déclencheur pour les opérations INSERT uniquement - en insérant des enregistrements dans SalesHistory table à partir de la table insérée.

Comme nous le savons, inséré est une table logique qui capture les changements qui se produisent dans la table Source (le tableau Ventes table dans notre cas).

Nous pouvons voir une autre table logique spéciale supprimée dans le déclencheur UPDATE car le supprimé table ne s'applique pas aux déclencheurs INSERT.

Ajoutons un nouvel enregistrement pour vérifier si des enregistrements sont insérés dans l'SalesHistory tableau automatiquement.

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

Même si nous n'avons inséré qu'un seul enregistrement dans le champ Ventes table, nous obtenons 2 lignes de la 1 ligne affectée message. Le deuxième enregistrement apparaît en raison de l'opération INSERT dans le cadre du déclencheur appelé par l'activité INSERT sur le Sales table - insertion d'un enregistrement dans l'SalesHistory tableau.

Vérifions les enregistrements à la fois sur les Ventes et Historique des ventes tableaux :

SELECT * 
FROM Sales

SELECT * 
FROM SalesHistory

Nous pouvons voir que ChangeDate et ChangedUser sont automatiquement remplis. C'est parce que nous avons conçu notre Histoire table avec les valeurs par défaut comme GETDATE() et SUSER_NAME() .

Les utilisateurs finaux peuvent voir via Trigger ou d'autres moyens que leur INSERT a été audité via la 1 ligne supplémentaire affectée message. Si vous souhaitez surveiller les modifications sans en informer les utilisateurs, vous devez appliquer le SET ROWCOUNT ON commande. Il supprime les résultats affichés pour les opérations DML qui se produisent à l'intérieur du déclencheur.

Modifions notre déclencheur en utilisant le script avec le SET ROWCOUNT ON option et voyez-la en action :

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

Maintenant, nous insérons un autre enregistrement dans les Ventes tableau :

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

Nous ne pouvons voir qu'une seule 1 ligne affectée message. Ainsi, le public cible peut ne pas être informé du tout que ses actions sont sous surveillance.

Vérifions si le déclencheur INSERT a été appelé en vérifiant les Sales et Historique des ventes tableaux.

Oui, l'événement INSERT sur les Sales table a été déclenchée avec succès. Les enregistrements ont été insérés dans l'SalesHistory table sans avertir les utilisateurs.

Par conséquent, si vous créez des déclencheurs à des fins d'audit, le SET NOCOUNT ON est nécessaire. Il permet d'auditer sans alerter personne.

Déclencheur de MISE À JOUR

Avant de créer un déclencheur UPDATE réel sur le Sales table, référons-nous à nouveau aux tables logiques spéciales insérées et supprimées. Créez un exemple de déclencheur UPDATE sur Sales tableau :

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

Le déclencheur UPDATE a été créé avec succès. Maintenant, insérons un nouvel enregistrement de manière incorrecte. Plus tard, nous le mettrons à jour pour vérifier le déclencheur UPDATE en action :

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

Nous avons les enregistrements ci-dessous dans les ventes et Historique des ventes tableau :

Mettons à jour SalesId =3 dans les Ventes tableau avec de nouvelles valeurs. Nous verrons les données dans les tableaux insérés et supprimés :

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

Lorsque l'opération UPDATE a lieu, toutes les valeurs nouvelles ou modifiées seront disponibles dans la table insérée, et les anciennes valeurs seront disponibles dans la table supprimée :

Maintenant, modifions le déclencheur UPDATE avec le script ci-dessous et vérifions-le en action :

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

Le déclencheur UPDATE a été modifié avec succès et nous pouvons exécuter à nouveau le même script UPDATE :

Maintenant, nous pouvons voir la 1 ligne affectée message deux fois. Il indique l'exécution de l'opération UPDATE sur le Sales table et l'opération INSERT sur SalesHistory table. Vérifions cela en sélectionnant dans les deux tableaux :

L'activité UPDATE a été suivie dans l'SalesHistory table comme un nouvel enregistrement. Avant cet enregistrement, nous en avons un autre indiquant quand l'enregistrement a été inséré en premier.

Déclencheur SUPPRIMER

Jusqu'à présent, nous avons testé le FOR ou APRÈS type de déclencheurs pour les opérations INSERT ou UPDATE. Maintenant, nous pouvons essayer d'utiliser le AU LIEU DE type de déclencheur DML pour l'opération DELETE. Utilisez le script ci-dessous :

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

Le déclencheur DELETE est créé avec succès. Il enverra un message d'erreur au client au lieu d'exécuter la commande DELETE sur le Sales tableau.

Essayons de supprimer l'enregistrement SalesID =3 des ventes table en utilisant le script ci-dessous :

DELETE FROM Sales
WHERE SalesId = 3

Nous avons empêché les utilisateurs de supprimer des enregistrements de Ventes table. Le déclencheur a généré un message d'erreur.

Vérifions également si l'enregistrement a été supprimé de la section Sales table et s'il y a eu des changements sur le SalesHistory tableau :

Comme nous avons exécuté le script de déclenchement avant l'instruction DELETE réelle à l'aide du déclencheur INSTEAD OF, l'opération DELETE sur SalesId=3 n'a pas réussi du tout. Par conséquent, aucun changement n'a été reflété à la fois dans les Ventes et Historique des ventes tableau.

Modifions le déclencheur à l'aide du script ci-dessous pour identifier la tentative de suppression sur la table :

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

Le déclencheur a été modifié avec succès. Supprimons le SalesId =3 enregistrement des ventes tableau à nouveau :

L'exécution de l'instruction DELETE affiche la 1 ligne affectée message deux fois. Vérifions les enregistrements dans l'ensemble des ventes et Historique des ventes tableaux pour voir exactement ce qui s'y passe :

La logique utilisée dans le déclencheur DELETE était de capturer toutes les tentatives de DELETE sur la table sans réellement supprimer l'enregistrement du Ventes tableau en utilisant AU LIEU DE gâchette. Nous pouvons confirmer que l'enregistrement n'a pas été supprimé de la section Sales table, et un nouvel enregistrement a été inséré dans le SalesHistory tableau.

Un déclencheur unique pour gérer les opérations INSERT, UPDATE et DELETE

Jusqu'à présent, nous avons créé 3 déclencheurs pour gérer les opérations INSERT, UPDATE et DELETE sur une seule table. Si nous avons plusieurs déclencheurs, il serait difficile de les gérer, surtout s'ils ne sont pas correctement documentés. Il peut y avoir des problèmes de performances si les développeurs ont utilisé une logique contradictoire sur plusieurs déclencheurs.

Personnellement, je recommande d'utiliser un seul déclencheur avec toute la logique combinée pour éviter les pertes de données potentielles ou les problèmes de performances. Nous pouvons essayer de combiner 3 déclencheurs en un seul déclencheur pour de meilleures performances. Mais avant de le faire, examinons comment SUPPRIMER les déclencheurs existants et comment désactiver ou activer les déclencheurs.

Laisser tomber le déclencheur

Pour fusionner 3 déclencheurs en un seul, nous devons d'abord supprimer ces 3 déclencheurs. C'est possible via les approches SSMS et T-SQL.

Dans SSMS, développez le Test base de données > Tableaux > Ventes tableau > Déclencheurs .

Nous pouvons voir nos 3 déclencheurs créés jusqu'à présent :

Pour supprimer un déclencheur, faites simplement un clic droit dessus > Supprimer > OK .

Si vous préférez utiliser T-SQL, consultez la syntaxe ci-dessous pour supprimer le déclencheur :

DROP TRIGGER <trigger_name>

Il y a les TR_INS_Sales déclencheur que nous avons créé sur le Sales table. Le script sera :

DROP TRIGGER TR_INS_Sales

Important  :la suppression d'une table supprime tous les déclencheurs par défaut.

Désactiver et activer le déclencheur

Au lieu de laisser tomber le déclencheur, nous pouvons le désactiver temporairement avec le Désactiver déclencheur option via SSMS ou T-SQL.

Dans SSMS, cliquez avec le bouton droit sur le nom du déclencheur> Désactiver . Une fois désactivé, le déclencheur ne sera pas déclenché tant que vous ne l'aurez pas réactivé.

Pendant que le déclencheur fonctionne, le Activer l'option est grisée. Lorsque vous le désactivez, le bouton Activer l'option deviendra visible et active.

Si vous préférez utiliser T-SQL, vous pouvez désactiver et activer les déclencheurs à l'aide des scripts ci-dessous :

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

Pour désactiver et activer nos TR_INS_Sales particuliers déclencheur sur les Ventes table, nous utilisons les scripts ci-dessous :

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

Ainsi, nous avons appris à DROP , DÉSACTIVER , et ACTIVER déclencheurs. Je vais supprimer 3 déclencheurs existants et créer un seul déclencheur couvrant les 3 opérations ou l'insertion, la mise à jour et la suppression à l'aide du script ci-dessous :

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 création du déclencheur unique a réussi. Nous avons utilisé la logique pour identifier l'opération en utilisant les tables insérées et supprimées.

Pour l'opération INSERT, la table supprimée ne sera pas remplie. Pour l'opération DELETE, la table insérée ne sera pas remplie. Nous pouvons facilement identifier ces opérations. Si ces 2 conditions ne correspondent pas, il s'agit d'une opération UPDATE et nous pouvons utiliser une simple instruction ELSE.

J'ai utilisé le UPDATE() fonction pour montrer comment cela fonctionne. S'il y avait des mises à jour sur ces colonnes, l'action du déclencheur UPDATE se déclencherait. Nous pouvons également utiliser le COLUMNS_UPDATED() fonction dont nous avons parlé plus tôt pour identifier également une opération UPDATE.

Testons notre nouveau déclencheur en insérant un nouvel enregistrement :

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

Vérification des enregistrements dans l'ensemble des ventes et Historique des ventes les tableaux montrent les données comme ci-dessous :

Essayons de mettre à jour SalesId =2 enregistrement :

UPDATE Sales
SET price = 250
WHERE SalesId = 2;

Essayons un script DELETE via cette procédure sur SalesId =4 enregistrement :

DELETE FROM Sales
WHERE SalesId = 4;

Comme nous pouvons le remarquer, SalesId =4 a été supprimé des Ventes table puisqu'il s'agit d'un POUR ou APRÈS déclencheur, faisant en sorte que l'opération DELETE réussisse sur le Sales table, puis insérez un enregistrement dans SalesHistory tableau.

Objectif des déclencheurs DML

Les déclencheurs DML sont efficaces dans les scénarios suivants :

  1. Suivez les modifications historiques des opérations INSERT, UPDATE et DELETE sur une table spécifique.
  2. Auditer les événements DML se produisant sur une table sans exposer l'activité d'audit aux utilisateurs.
  3. Empêchez les modifications DML de se produire sur une table via AU LIEU DE déclenche et alerte les utilisateurs avec un message d'erreur spécifique.
  4. Envoyer des notifications aux personnes ciblées lorsque des conditions prédéfinies sont atteintes.
  5. Démarrez le travail de l'Agent SQL Server ou tout autre processus chaque fois que vous remplissez des conditions prédéfinies.

Et vous pouvez les utiliser pour toute autre exigence de logique métier que vous pouvez implémenter avec des instructions T-SQL.

Conclusion

Le corps du déclencheur DML est similaire à la procédure stockée. Nous pouvons implémenter n'importe quelle logique métier requise, mais nous devons être prudents lors de l'écriture de cette logique impliquée pour éviter les problèmes potentiels.

Même si SQL Server prend en charge la création de plusieurs déclencheurs sur une seule table, il est préférable de consolider en un seul déclencheur. De cette façon, vous pouvez gérer facilement les déclencheurs et les dépanner plus rapidement. Chaque fois que des déclencheurs DML sont implémentés à des fins d'audit, assurez-vous que l'option SET NOCOUNT ON l'option est utilisée efficacement.

Dans le prochain article, nous examinerons les déclencheurs DDL et les déclencheurs de connexion.