Trigger Ensembliste

Suivant les expériences et sensibilités de chacun, les Déclencheurs (Triggers en anglais) sont acceptés et utilisés par certains, ou fuis comme la peste par d’autres.
Outre l’architecture technique permettant parfois d’exclure les codages sous forme de déclencheurs, il est aussi indispensable de bien comprendre le principe de fonctionnement des déclencheurs afin de ne pas laisser traîner des bugs qui seront très difficiles à corriger.

Nous ne nous intéresserons ici qu’aux déclencheurs de type DML (Data Modification Language), car c’est ce type de déclencheurs qui peut le plus souvent être mal maîtrisé et au final devenir contre-productif.

Premièrement, qu’est-ce qu’un déclencheur ? Et bien c’est un bloc de code qui s’exécute automatiquement, de manière totalement implicite, après ou à la place d’une instruction de modification (INSERT, DELETE ou UPDATE) dans une table donnée. Ce déclencheur s’exécutera automatiquement pour chaque instruction concernée. Prenons par exemple le code suivant (je m’appuierai ici sur les bases d’exemple issues de http://msftdbprodsamples.codeplex.com/) :

CREATE TRIGGER Sales.trigCurrency
   ON  Sales.Currency
   AFTER INSERT
AS
BEGIN
	DECLARE @name nvarchar(50)
	SELECT @name = Name
	FROM inserted
	IF len(@name) < 5
	BEGIN
		ROLLBACK TRANSACTION
	END
END

Ce déclencheur s’exécute automatiquement lors de chaque instruction INSERT sur la table Sales.Currency. On remarquera donc que si les échanges entre le client et le serveur SQL se fait sous forme CRUD (Create / Read / Update / Delete), avec des instructions unitaires, le coût d’exécution du déclencheur peut être non négligeable. Ainsi, si 20 instructions INSERT sont exécutées pour définir 20 nouvelles devises, le déclencheur sera exécuté 20 fois.

set nocount on
declare @compteur int=0
while @compteur<20
begin
	insert into Sales.Currency (CurrencyCode,Name,ModifiedDate)
		select N'X'+convert(nvarchar(2),@compteur),
				N'DEVISE'+convert(nvarchar(2),@compteur),
				getdate()
	select @compteur+=1
end

Et c’est justement dans une mauvaise compréhension de ce mode de déclenchement que résident souvent quelques bugs.
Dans le cas présent, lors de l’insertion d’un nouvel enregistrement dans la table Sales.Currency, la valeur de la colonne Name est stockée dans une variable @Name, dont la longueur est ensuite déterminée, et si cette longueur est inférieure à 5 alors le système signale une erreur et l’insertion est refusée.

Mais que se passe-t-il lorsque plusieurs enregistrements sont insérés en une seule instruction ?

On remarque ici qu’un enregistrement incorrect s’est glissé au milieu du lot et n’a pas été intercepté par le déclencheur. La raison en est simple : le code du déclencheur a été exécuté une et une seule fois lors de l’instruction INSERT (et non pas une fois par ligne insérée). La valeur de la variable @Name n’a donc pas spécialement été maîtrisée (en fait, c’est la valeur de la première ligne qui a été prise en compte). Et au final, le déclencheur semble de pas avoir fait son travail.
En fait, il ne faut pas oublier ce que représente « inserted » dans le déclencheur. Il s’agit d’une table contenant les valeurs insérées, avec les colonnes correspondant à la table Sales.Currency, mais surtout avec autant de lignes qu’il se doit pour décrire l’ensemble de l’impact de l’instruction INSERT, et il ne faut surtout pas oublier que l’instruction INSERT permet tout à fait d’insérer plusieurs lignes d’un coup. Il s’agit donc de modifier le code du trigger pour prendre en compte cet état de fait, et faire en sorte que le déclencheur joue son rôle non seulement pour les insertions unitaires, mais aussi pour les insertions multiples.

ALTER TRIGGER Sales.trigCurrency
   ON  Sales.Currency
   AFTER INSERT
AS
BEGIN
	IF EXISTS (select *
				from inserted
				where len(Name) < 5)
	BEGIN
		ROLLBACK TRANSACTION
	END
END

Et cette fois-ci, plus moyen de cacher une valeur incorrecte au milieu d’un lot de bonnes données !

D’une manière générale, on peut dire qu’à partir du moment où l’on se met à déclarer des variables dans un déclencheur, il y a de fortes chances que l’on ait implémenté un code qui gère les données de manière unitaire et oublie totalement le fait que les instructions SQL peuvent manipuler plusieurs enregistrements d’un coup. Donc ne fuyez pas forcément les déclencheurs, mais soyez vigilants dans leur implémentation…

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Etes-vous un robot ? *Chargement du capcha...

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.