284
Chapitre 8. Optimisation du code SQL
Sans la partie INTO, OUTPUT retourne le jeu de résultat à la session. Attention, dans
ce cas il ne peut y avoir de déclencheur pour la même instruction.
Nous avons pour l’instant traité de déclencheur de type AFTER, c’est-à-dire s’exécutant après la modification de données. Il existe un autre type de déclencheur : INSTEAD OF. Celui-ci s’exécute non pas avant l’instruction (il n’existe pas de trigger
BEFORE en SQL Server), mais à la place. Si vous voulez que l’instruction qui a déclenché le trigger s’exécute réellement, vous devez la réécrire dans le corps du trigger, en
vous basant sur le contenu des pseudo-tables. Ce type de déclencheur est utile pour
réorienter le résultat d’une instruction, ou ne traiter qu’une partie de celle-ci. Un de
ses intérêts principaux est qu’il peut être placé sur une vue, permettant alors une gestion fine des mises à jour à travers la vue.
Enfin, ajoutons que souvent les déclencheurs sont utilisés pour effectuer des contrôles de cohérence. Autant que possible, préférez les contraintes CHECK aux triggers.
Les contraintes CHECK sont trop souvent sous-estimées. Elles peuvent contenir des
expressions complexes portant sur toutes les colonnes de la ligne. Si vous devez
effectuer des contrôles à partir de données hors de la table, vous pouvez tricher en
passant par une fonction utilisateur. Si vous devez faire des vérifications sur un
ensemble de lignes de la table, par contre le déclencheur sera probablement plus
optimal, de par son comportement ensembliste. La fonction utilisateur, elle, devra
être appelée ligne par ligne. Voici, par exemple, un code de trigger permettant de
vérifier que des dates de validité d’historique ne se chevauchent pas (attention, ce
code présuppose que les colonnes fromDate et toDate n’acceptent pas les NULL) :
ALTER TRIGGER atr_iu$sales_CurrencyHistory$checkConsistency
ON sales.CurrencyHistory
FOR INSERT, UPDATE
AS BEGIN
IF @@ROWCOUNT = 0 RETURN
SET NOCOUNT ON
IF EXISTS (
SELECT 1
FROM sales.CurrencyHistory ch WITH (READUNCOMMITTED)
JOIN inserted i
ON ch.CurrencyCode = i.CurrencyCode AND
ch.fromDate <> i.fromDate
WHERE
( ch.fromDate BETWEEN i.fromDate AND i.toDate OR
ch.toDate BETWEEN i.fromDate AND i.toDate ) OR
( i.fromDate BETWEEN ch.fromDate AND ch.toDate OR
i.toDate
BETWEEN ch.fromDate AND ch.toDate )
)
BEGIN
RAISERROR ('Des dates d''historique se chevauchent !', 16, 10)
RETURN
END
END
Chapitre 8. Optimisation du code SQL
Sans la partie INTO, OUTPUT retourne le jeu de résultat à la session. Attention, dans
ce cas il ne peut y avoir de déclencheur pour la même instruction.
Nous avons pour l’instant traité de déclencheur de type AFTER, c’est-à-dire s’exécutant après la modification de données. Il existe un autre type de déclencheur : INSTEAD OF. Celui-ci s’exécute non pas avant l’instruction (il n’existe pas de trigger
BEFORE en SQL Server), mais à la place. Si vous voulez que l’instruction qui a déclenché le trigger s’exécute réellement, vous devez la réécrire dans le corps du trigger, en
vous basant sur le contenu des pseudo-tables. Ce type de déclencheur est utile pour
réorienter le résultat d’une instruction, ou ne traiter qu’une partie de celle-ci. Un de
ses intérêts principaux est qu’il peut être placé sur une vue, permettant alors une gestion fine des mises à jour à travers la vue.
Enfin, ajoutons que souvent les déclencheurs sont utilisés pour effectuer des contrôles de cohérence. Autant que possible, préférez les contraintes CHECK aux triggers.
Les contraintes CHECK sont trop souvent sous-estimées. Elles peuvent contenir des
expressions complexes portant sur toutes les colonnes de la ligne. Si vous devez
effectuer des contrôles à partir de données hors de la table, vous pouvez tricher en
passant par une fonction utilisateur. Si vous devez faire des vérifications sur un
ensemble de lignes de la table, par contre le déclencheur sera probablement plus
optimal, de par son comportement ensembliste. La fonction utilisateur, elle, devra
être appelée ligne par ligne. Voici, par exemple, un code de trigger permettant de
vérifier que des dates de validité d’historique ne se chevauchent pas (attention, ce
code présuppose que les colonnes fromDate et toDate n’acceptent pas les NULL) :
ALTER TRIGGER atr_iu$sales_CurrencyHistory$checkConsistency
ON sales.CurrencyHistory
FOR INSERT, UPDATE
AS BEGIN
IF @@ROWCOUNT = 0 RETURN
SET NOCOUNT ON
IF EXISTS (
SELECT 1
FROM sales.CurrencyHistory ch WITH (READUNCOMMITTED)
JOIN inserted i
ON ch.CurrencyCode = i.CurrencyCode AND
ch.fromDate <> i.fromDate
WHERE
( ch.fromDate BETWEEN i.fromDate AND i.toDate OR
ch.toDate BETWEEN i.fromDate AND i.toDate ) OR
( i.fromDate BETWEEN ch.fromDate AND ch.toDate OR
i.toDate
BETWEEN ch.fromDate AND ch.toDate )
)
BEGIN
RAISERROR ('Des dates d''historique se chevauchent !', 16, 10)
RETURN
END
END
