2009-10-07 4 views
21

J'ai le déclencheur suivant sur une table pour une base de données SQL Server 2008. C'est récurent, alors j'ai besoin de l'arrêter. Après avoir inséré ou mis à jour un enregistrement, j'essaye simplement de mettre à jour un seul champ sur cette table.Comment empêcher un déclencheur de base de données d'être récursif?

Voici le déclencheur:

ALTER TRIGGER [dbo].[tblMediaAfterInsertOrUpdate] 
    ON [dbo].[tblMedia] 
    BEFORE INSERT, UPDATE 
AS 
BEGIN 
    SET NOCOUNT ON 

    DECLARE @IdMedia INTEGER, 
     @NewSubject NVARCHAR(200) 

    SELECT @IdMedia = IdMedia, @NewSubject = Title 
    FROM INSERTED 

    -- Now update the unique subject field. 
    -- NOTE: dbo.CreateUniqueSubject is my own function. 
    --  It just does some string manipulation. 
    UPDATE tblMedia 
    SET UniqueTitle = dbo.CreateUniqueSubject(@NewSubject) + 
         CAST((IdMedia) AS VARCHAR(10)) 
    WHERE tblMedia.IdMedia = @IdMedia 
END 

Quelqu'un peut-il me dire comment je peux empêcher l'insertion de la gâchette du coup d'envoi un autre déclencheur à nouveau?

+0

Un certain nombre de personnes ont dit désactiver la récursivité de déclenchement. en ce moment, je ne vais pas vouloir toucher ce paramètre. Je préfère réparer le tsql. –

+0

Puis peraps la gâchette ne doit pas être avant, mais un déclencheur INSTEAD OF? http://msdn.microsoft.com/en-us/library/ms175089.aspx –

Répondre

1

Vous pouvez avoir une colonne NULLABLE distincte indiquant si UniqueTitle a été défini.

Set à valeur réelle dans un déclencheur, et ont la gâchette ne rien faire si sa valeur est vrai dans « INSERE »

+0

Pourquoi le vote négatif? – DVK

30

Je vois trois possibilités:

  1. Désactiver déclencheur récursion:

    Ceci empêchera un déclencheur d'appeler un autre déclencheur ou de s'appeler à nouveau. Pour ce faire, exécutez la commande suivante:

    ALTER DATABASE MyDataBase SET RECURSIVE_TRIGGERS OFF 
    GO 
    
  2. Utiliser un déclencheur INSTEAD OF UPDATE, INSERT

    L'utilisation d'un INSTEAD OF déclencheur vous pouvez contrôler toute colonne mise à jour/inséré, et même remplaçant avant d'appeler la commander.

  3. contrôle la gâchette en empêchant l'utilisation IF UPDATE

    Test de la colonne vous dira avec une précision raisonnable si vous déclenchez est lui-même appeler. Pour ce faire, utilisez la clause IF UPDATE() comme:

    ALTER TRIGGER [dbo].[tblMediaAfterInsertOrUpdate] 
        ON [dbo].[tblMedia] 
        FOR INSERT, UPDATE 
    AS 
    BEGIN 
        SET NOCOUNT ON 
        DECLARE @IdMedia INTEGER, 
         @NewSubject NVARCHAR(200) 
    
        IF UPDATE(UniqueTitle) 
         RETURN; 
    
        -- What is the new subject being inserted? 
        SELECT @IdMedia = IdMedia, @NewSubject = Title 
        FROM INSERTED 
    
        -- Now update the unique subject field. 
        -- NOTE: dbo.CreateUniqueSubject is my own function. 
        --  It just does some string manipulation. 
        UPDATE tblMedia 
        SET UniqueTitle = dbo.CreateUniqueSubject(@NewSubject) + 
             CAST((IdMedia) AS VARCHAR(10)) 
        WHERE tblMedia.IdMedia = @IdMedia 
    END 
    
+0

Question rapide. J'utilise _BEFORE_ au lieu de _AFTER_. Cela va-t-il encore me donner la nouvelle valeur d'ID Inséré (Identité)? Ou est-ce seulement créé sur un _AFTER_ ?? –

+0

Fixé. Allumez le déclencheur après l'insertion/mise à jour – Rodrigo

+0

Nope - ne fonctionne pas. Je fais un insert mais la mise à jour (UniqueTitle) doit penser que c'est une mise à jour ... ??? –

7
ALTER DATABASE <dbname> SET RECURSIVE_TRIGGERS OFF 

RECURSIVE_TRIGGERS { ON | OFF }

ON tir récursive des déclencheurs AFTER est autorisé.

OFF Seul le déclenchement récursif direct des déclencheurs AFTER n'est pas autorisé. Pour également désactiver la récursivité indirecte de déclencheurs AFTER , définissez l'option de serveur de déclencheurs imbriqués à 0 en utilisant sp_configure.

Seule la récursion directe est empêchée lorsque RECURSIVE_TRIGGERS est réglé sur OFF. Pour désactiver la récursivité indirecte, vous devez également définir l'option de serveur déclencheurs imbriqués sur 0.

L'état de cette option peut être déterminée en examinant la colonne is_recursive_triggers_on dans la vue catalogue sys.databases ou la propriété IsRecursiveTriggersEnabled de la fonction DATABASEPROPERTYEX.

5

Je pense que je l'ai :)

Lorsque le titre est de se « mis à jour » (lisez: inséré ou mis à jour), puis mettre à jour le sujet unique. Lorsque le déclencheur est exécuté une deuxième fois, le champ unique objet est mis à jour, il s'arrête et quitte le déclencheur.

Aussi, je l'ai fait gérer de multiples lignes qui sont modifiées -> Je oublie toujours à ce sujet avec des déclencheurs.

ALTER TRIGGER [dbo].[tblMediaAfterInsert] 
    ON [dbo].[tblMedia] 
    FOR INSERT, UPDATE 
AS 
BEGIN 
    SET NOCOUNT ON 

    -- If the Title is getting inserted OR updated then update the unique subject. 
    IF UPDATE(Title) BEGIN 
     -- Now update all the unique subject fields that have been inserted or updated. 
     UPDATE tblMedia 
     SET UniqueTitle = dbo.CreateUniqueSubject(b.Title) + 
          CAST((b.IdMedia) AS VARCHAR(10)) 
     FROM tblMedia a 
      INNER JOIN INSERTED b on a.IdMedia = b.IdMedia 
    END 
END 
+0

Je ne sais pas pourquoi cela a été rabaissé par n'importe qui, c'est une solution assez utile. –

0

Par souci d'exhaustivité, je vais ajouter quelques petites choses. Si vous avez un trigger après particulier que vous ne souhaitez exécuter qu'une seule fois, vous pouvez le configurer pour qu'il s'exécute en dernier à l'aide de sp_settriggerorder.

Je considérerais également s'il ne serait pas préférable de combiner les déclencheurs qui effectuent la récursion en un seul déclencheur.

41

Je ne sais pas si elle est pertinente à la question de l'OP plus, mais si vous êtes venu ici pour savoir comment empêcher récursion ou récursion mutuelle de se produire dans un déclencheur, vous pouvez tester cela comme ceci:

IF TRIGGER_NESTLEVEL() <= 1/*this update is not coming from some other trigger*/ 

MSDN link

+5

C'est en fait la meilleure réponse, et celle qui répond le plus directement à la question de l'auteur. – Curt

+0

oui cela devrait être la réponse. merci –

+1

Comme la réponse indique effectivement, cela empêche également le déclenchement du déclencheur si la mise à jour provient d'un autre déclencheur. Mais ce n'est pas une récursivité. La récursivité se produit lorsque la mise à jour provient du même déclencheur. Dans ce cas, vous devez transmettre l'identifiant de l'objet à la fonction: https://stackoverflow.com/a/47074365/150342 – Colin

1

TRIGGER_NESTLEVEL peut être utilisé pour prévenir la récursivité d'un déclencheur spécifique, mais il est important de transmettre l'ID d'objet de la détente dans la fonction. Sinon, vous aussi empêcher le déclenchement du tir lorsqu'une insertion ou mise à jour est faite par un autre déclencheur:

IF TRIGGER_NESTLEVEL(OBJECT_ID('dbo.mytrigger')) > 1 
     BEGIN 
      PRINT 'mytrigger exiting because TRIGGER_NESTLEVEL > 1 '; 
      RETURN; 
    END; 

De MSDN:

Si aucun paramètres sont spécifiés, TRIGGER_NESTLEVEL renvoie le nombre total de déclencheurs sur la pile d'appels. Cela comprend lui-même.

Référence: Avoiding recursive triggers

Questions connexes