2017-04-05 1 views
3

Tout premier poste, s'il vous plaît soyez doux ...SQL Server: option permettant de spécifier si la transaction de mise à jour provenait du déclencheur?

J'ai besoin de mettre à jour une colonne lorsqu'une table est soit mis à jour ou ligne (s) inséré, donc je l'ai créé un déclencheur (AFTER INSERT, UPDATE). Le problème est qu'il est récursif en raison du fait que l'insertion inclut une instruction de mise à jour, déclenchant ainsi le déclencheur à nouveau.

J'ai aussi essayé séparer le INSERT et UPDATE en deux déclencheurs différents, mais je l'ai rencontré un problème sp_settriggerorder() et trigger_nestlevel(), parce qu'il ya d'autres déclencheur en place, en raison de défauts d'application de la boîte.

Ma question est, est-il possible d'utiliser une clause IF indiquant si la mise à jour provient de l'application elle-même ou de mon déclencheur? Cas, si c'est mon déclencheur, que je pourrais facilement SAUF s'il y a un retour et il ne serait plus récursif.

CREATE TRIGGER [dbo].[JobCardMetlInsertUpdateItemDesc] 
ON [dbo].[JobCardMetl] AFTER INSERT 
AS 
    BEGIN TRANSACTION [Description] 

    UPDATE JobCardMetl 
    SET JobCardMetl.Description = item.Description 
    FROM JobCardMetl 
    INNER JOIN item ON JobCardMetl.Item = item.item 
    WHERE JobCardMetl.RecordDate = (SELECT MAX(JobCardMetl.RecordDate) 
            FROM JobCardMetl) 

    COMMIT TRANSACTION [Description] 
+0

récursion Trigger (un déclencheur de déclenchement directement lui-même) peut être désactivé au niveau de la base de données ou du serveur (?). Bien sûr, cela affecte tout. – RBarryYoung

Répondre

3

Votre déclencheur est très suspect: Il ne fait pas référence à la INSERTED pseudotable. Cela signifie que votre déclencheur met à jour les enregistrements non affectés par l'INSERT, toujours une énorme odeur de code.

La solution habituelle au problème des déclencheurs récursifs est de faire attention aux colonnes qui sont mises à jour, c'est-à-dire. utilisez UPDATED(), et quelles lignes, et la logique métier naturelle doit arrêter la récursivité (le déclencheur imbriqué ne doit rien trouver à mettre à jour, car les vérifications de garde ne sont pas qualifiées). En fin de compte, vous pouvez utiliser le marteau logique: SET CONTEXT_INFO et CONTEXT_INFO() En fin de compte, vous pouvez utiliser le marteau logique: SET CONTEXT_INFO et CONTEXT_INFO(). Vous vérifiez, réglez-le et nettoyez-le dans votre déclencheur. Si est déjà défini, vous savez que vous êtes imbriqué à partir du déclencheur. La partie de nettoyage est critique. Vous priez également qu'aucun autre app/dev ne fasse la même chose, car il n'y a qu'une seule information de contexte par session (SQL 2016 l'améliore).

0

Vous pouvez vérifier si la description est différente de ce que vous voulez qu'elle soit mise à jour. Si la même chose, vous ne mettez pas à jour. De cette façon, vous évitez la récursivité sans fin. En outre, avec la condition WHERE, vous semblez vouloir limiter la mise à jour à l'enregistrement actuellement inséré, mais pour cela, vous pouvez utiliser la table virtuelle INSERTED, qui contient les enregistrements qui ont été insérés. Enfin, il semble exagéré de commencer une nouvelle transaction pour une instruction atomique. Notez que le déclencheur s'exécutera de toute façon dans la transaction dans laquelle l'instruction INSERT de déclenchement s'exécute.

Donc, en prenant tout cela ensemble, vous pouvez faire votre déclencheur comme suit (je suppose RecordDate identifie de manière unique un enregistrement - changer à tout ce qui est la clé primaire):

CREATE TRIGGER [dbo].[JobCardMetlInsertUpdateItemDesc] 
ON [dbo].[JobCardMetl] AFTER INSERT 
AS 
    UPDATE JobCardMetl 
    SET j.Description = item.Description 
    FROM JobCardMetl j 
    INNER JOIN item ON j.Item = item.item 
    INNER JOIN INSERTED i ON i.RecordDate = j.RecordDate 
    WHERE j.Description IS NULL OR j.Description <> item.Description