web-dev-qa-db-fra.com

SQL Server - après le déclencheur d'insertion - mettez à jour une autre colonne dans la même table

J'ai ce déclencheur de base de données:

CREATE TRIGGER setDescToUpper
ON part_numbers
 AFTER INSERT,UPDATE
AS
DECLARE @PnumPkid int, @PDesc nvarchar(128)

SET @PnumPkid = (SELECT pnum_pkid FROM inserted)
SET @PDesc = (SELECT UPPER(part_description) FROM inserted)

UPDATE part_numbers set part_description_upper = @PDesc WHERE pnum_pkid=@PnumPkid

GO

Est-ce une mauvaise idée? C'est pour mettre à jour une colonne sur la même table. Je veux qu'il se déclenche pour l'insertion et la mise à jour.

Ça marche, j'ai juste peur d'une situation cyclique. La mise à jour, à l'intérieur du déclencheur, déclenche le déclencheur, encore et encore. Cela arrivera-t-il?

S'il vous plaît, ne vous attardez pas sur les majuscules. Situation folle.

24
Lance Perry

Cela dépend du niveau de récursivité des déclencheurs actuellement définis sur la base de données.

Si tu fais ça:

SP_CONFIGURE 'nested_triggers',0
GO
RECONFIGURE
GO

Ou ca:

ALTER DATABASE db_name
SET RECURSIVE_TRIGGERS OFF

Ce déclencheur ci-dessus ne sera plus appelé, et vous seriez en sécurité (à moins que vous ne tombiez dans une sorte d'impasse; cela pourrait être possible mais je me trompe peut-être).

Pourtant, je ne pense pas que ce soit une bonne idée. Une meilleure option serait d'utiliser un INSTEAD OF trigger . De cette façon, vous évitez d'exécuter la première mise à jour (manuelle) sur la base de données. Seul celui défini à l'intérieur du déclencheur serait exécuté.

Un déclencheur INSTEAD OF INSERT serait comme ceci:

CREATE TRIGGER setDescToUpper ON part_numbers
INSTEAD OF INSERT
AS
BEGIN
    INSERT INTO part_numbers (
        colA,
        colB,
        part_description
    ) SELECT
        colA,
        colB,
        UPPER(part_description)
    ) FROM
        INSERTED
END
GO

Cela "remplacerait" automatiquement l'instruction INSERT d'origine par celle-ci, avec un appel UPPER explicite appliqué à part_description champ.

Un déclencheur INSTEAD OF UPDATE serait similaire (et je ne vous conseille pas de créer un seul déclencheur, gardez-les séparés).

En outre, cela répond au commentaire de @Martin: cela fonctionne pour les insertions/mises à jour à plusieurs lignes (votre exemple ne fonctionne pas).

39
rsenna

Une autre option serait de placer l'instruction de mise à jour dans une instruction IF et d'appeler TRIGGER_NESTLEVEL () pour restreindre la mise à jour en cours d'exécution une deuxième fois.

CREATE TRIGGER Table_A_Update ON Table_A AFTER UPDATE 
AS
IF ((SELECT TRIGGER_NESTLEVEL()) < 2)
BEGIN
    UPDATE a
    SET Date_Column = GETDATE()
    FROM Table_A a
    JOIN inserted i ON a.ID = i.ID
END

Lorsque le déclencheur s'exécute initialement, TRIGGER_NESTLEVEL est défini sur 1 afin que l'instruction de mise à jour soit exécutée. Cette instruction de mise à jour déclenchera à son tour le même déclencheur, sauf que cette fois TRIGGER_NESTLEVEL est défini sur 2 et l'instruction de mise à jour ne sera pas exécutée.

Vous pouvez également vérifier d'abord le TRIGGER_NESTLEVEL et s'il est supérieur à 1, puis appeler RETURN pour quitter le déclencheur.

IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;
15
Jorriss

Utilisez plutôt une colonne calculée. Il est presque toujours préférable d'utiliser une colonne calculée qu'un déclencheur.

Voir l'exemple ci-dessous d'une colonne calculée à l'aide de la fonction UPPER:

create table #temp (test varchar (10), test2 AS upper(test))
insert #temp (test)
values ('test')
select * from #temp

Et ne pas sonner comme un disque cassé ou quoi que ce soit, mais c'est extrêmement important. N'écrivez jamais un déclencheur qui ne fonctionnera pas correctement sur plusieurs insertions/mises à jour/suppressions d'enregistrement. Il s'agit d'une pratique extrêmement médiocre car tôt ou tard l'un d'eux se produira et votre déclencheur entraînera des problèmes d'intégrité des données car il n'échouera pas précisément, il exécutera uniquement le processus sur l'un des enregistrements. Cela peut durer longtemps jusqu'à ce que quelqu'un découvre le gâchis et, par eux, il est souvent impossible de corriger correctement les données.

6
HLGEM

Oui ... avoir une étape supplémentaire pour mettre à jour une table dans laquelle vous pouvez définir la valeur dans l'insert initial est probablement un processus supplémentaire et évitable. Avez-vous accès à l'instruction d'insertion d'origine où vous pouvez simplement insérer la partie_description dans la colonne part_description_upper à l'aide de la valeur UPPER (part_description)?

Après réflexion, vous n'avez probablement pas accès comme vous l'auriez probablement fait, vous devriez donc également donner quelques options ...

1) Dépend de la nécessité de cette colonne part_description_upper, si ce n'est que pour la "visualisation", vous pouvez simplement utiliser la valeur renvoyée part_description et "ToUpper ()" (en fonction du langage de programmation).

2) Si vous souhaitez éviter le traitement "en temps réel", vous pouvez simplement créer un travail sql pour parcourir vos valeurs une fois par jour pendant les périodes de faible trafic et mettre à jour cette colonne à la valeur UPPER part_description pour celles qui ne sont actuellement pas définies.

3) allez avec votre déclencheur (et surveillez la récursivité comme d'autres l'ont mentionné) ...

HTH

Dave

1
Dave

Oui, il appellera récursivement votre déclencheur, sauf si vous désactivez le paramètre des déclencheurs récursifs:

ALTER DATABASE db_name SET RECURSIVE_TRIGGERS OFF 

MSDN a une bonne explication du comportement à http://msdn.Microsoft.com/en-us/library/aa258254 (SQL.80) .aspx sous l'en-tête Déclencheurs récursifs.

1
Paul Kearney - pk

Il peut être plus sûr de quitter la gâchette lorsqu'il n'y a rien à faire. La vérification du niveau imbriqué ou la modification de la base de données en désactivant RECURSIVE peut être sujette à des problèmes.

Ms sql fournit un moyen simple, dans un déclencheur, pour voir si des colonnes spécifiques ont été mises à jour. Utilisez la méthode UPDATE () pour voir si certaines colonnes ont été mises à jour telles que UPDATE (part_description_upper).

IF UPDATE(part_description_upper)
  return
1
Visual Micro