Comment un déclencheur T-SQL qui se déclenche uniquement sur des modifications réelles est-il effectué?

9

J'ai un déclencheur de table sur UPDATE et INSERT qui ajoute une ligne à une autre table. Il n'a besoin d'ajouter une ligne que si l'une des quatre colonnes est modifiée. J'ai essayé d'utiliser IF UPDATE (col) pour tester les changements mais il a un angle mort. Il teste seulement qu'une certaine valeur est entrée. J'ai besoin d'approfondir, j'ai besoin de comparer les anciennes et les nouvelles valeurs pour voir qu'un vrai changement s'est produit. Il doit fonctionner avec INSERT et UPDATE.

Dans le cas d'une MISE À JOUR, c'est facile car les tables insérées et supprimées ont des valeurs que je peux comparer dans le déclencheur. Cependant, pour INSERT, seule la table d'insertion a des valeurs. Parce que j'ai besoin de tout cela dans le même déclencheur, comment gérer ce cas INSERT?

Voici le script du déclencheur que je souhaite modifier:

ALTER TRIGGER [dbo].[trATPerson_alter] 
   ON  [mydb].[dbo].[AT_Person]
   AFTER INSERT,UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    -- Not all updates require a push
    IF (UPDATE([First_Name]) OR UPDATE([Last_Name]) OR UPDATE([JobCode]) OR UPDATE([Inactive]))
    BEGIN
        INSERT INTO [mydb].[dbo].[AT_Person_To_Push] (
                [Facility],
                [VendorID],
                [Person_code],
                [First_Name],
                [Last_Name],
                [JobCode],
                [Alink],
                [Inactive]
            )
        SELECT  [Facility],
                [VendorID],
                [Person_code],
                [First_Name],
                [Last_Name],
                [JobCode],
                [Alink],
                [Inactive]
        FROM inserted 
    END
END
WillG
la source
2
Un petit mot sur l'utilisation de "IF UPDATE (<column>)". Il renvoie vrai si le DML spécifie une valeur pour la colonne, que la valeur ait réellement changé ou non.
Jonathan Fite

Réponses:

18

Vous pouvez gérer à la fois INSERT et UPDATE avec un opérateur set EXCEPT. Les EXISTS n'évalueront à VRAI que si c'est juste un INSERT, ou si c'est une MISE À JOUR avec des valeurs différentes pour l'une de ces colonnes.

IF EXISTS (
           SELECT First_Name, Last_Name, JobCoe, Inactive FROM inserted
           EXCEPT
           SELECT First_Name, Last_Name, JobCoe, Inactive FROM deleted
          )
BEGIN...
SQLRaptor
la source
C'est beaucoup plus élégant que de regarder les différentes fonctions mises à jour des colonnes. Nous avons combiné ceux-ci avec du code frontal pour n'envoyer que les valeurs modifiées (après de nombreuses querelles). L'utilisation d'un EXCEPT est beaucoup plus logique.
Peter Schott
2
Cela ne fonctionne pas dans les cas où 2 lignes sont «permutées» dans une mise à jour. Si nous avons deux John Smith qui ont besoin de mettre à jour leur JobCode (le premier John de 1 à 2; le second John de 2 à 1) - cela voudrait dire qu'aucune mise à jour ne s'est produite.
Steven Hibble
2
@StevenHibble - Dans la mesure du possible, quelle est la probabilité que cela se produise? Ce cas pourrait facilement être résolu en incluant les colonnes PK dans les instructions Select ci-dessus.
Chad Estes
1
Je dirais que la probabilité dépend de la source de données et de la probabilité d'une mauvaise entrée de données. "Oups, mal John Smith ..." ne semble pas que cela n'arrivera jamais . Dans tous les cas, cela ne concerne pas l'autre moitié d'une mise à jour multi-lignes: comment vous assurez-vous d'insérer uniquement les lignes qui changent? Cela EXISTSvérifie que toute ligne a changé. Si vous conservez l'insertion de la question, vous enregistrerez alors toutes les lignes mises à jour lorsqu'une seule change de manière significative.
Steven Hibble
2

Dans le cas où une mise à jour peut affecter plusieurs lignes, vous devez vous protéger contre deux choses:

  1. Nous voulons considérer les mises à jour qui échangent des valeurs entre des lignes similaires. S'il y a deux John Smith qui ont besoin de leur JobCodes mis à jour (le premier John de 1 à 2; le second John de 2 à 1), nous devons faire attention à dire qu'ils ont tous les deux été mis à jour.
  2. Nous voulons uniquement connecter les lignes modifiées AT_Person_To_Push. Si 5 lignes sont mises à jour, mais seulement 2 sont mises à jour d'une manière qui nous tient à cœur, alors nous devons traiter uniquement les 2 lignes pertinentes.

Voici comment je le gérerais:

  1. Jointure gauche insertedà deleted, car insertedaura des lignes pour les insertions et les mises à jour tandis que deletedn'aura que des lignes pour les mises à jour.
  2. Utilisez EXISTSavec EXCEPTpour rechercher les lignes où les insertedvaleurs diffèrent des deletedvaleurs. Vous ne pouvez pas l'utiliser i.First_Name != d.First_Name OR i.Last_Name != d.Last_Name...car la table supprimée sera vide (et LEFT JOIN renverra des valeurs nulles) lorsque le déclencheur gère un INSERT.
  3. Insérez uniquement les lignes affectées dans AT_Person_To_Push.
ALTER TRIGGER [dbo].[trATPerson_alter] 
   ON  [mydb].[dbo].[AT_Person]
   AFTER INSERT,UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    INSERT INTO [mydb].[dbo].[AT_Person_To_Push] (
            [Facility],
            [VendorID],
            [Person_code],
            [First_Name],
            [Last_Name],
            [JobCode],
            [Alink],
            [Inactive]
        )
    SELECT  i.[Facility],
            i.[VendorID],
            i.[Person_code],
            i.[First_Name],
            i.[Last_Name],
            i.[JobCode],
            i.[Alink],
            i.[Inactive]
    FROM inserted i
         LEFT JOIN deleted d
           ON i.Person_code = d.Person_code
    -- Check for changes that require a push
    WHERE EXISTS (SELECT i.[First_Name], i.[Last_Name], i.[JobCode], i.[Inactive]
                  EXCEPT
                  SELECT d.[First_Name], d.[Last_Name], d.[JobCode], d.[Inactive]);
END
Steven Hibble
la source
1

Essaye ça,

Declare @Acton int=0

If exists (Select 1 from inserted)
set @Acton=1

If exists (Select 1 from deleted)
set @Acton=@Acton+2

if(@Action=1) -- Only insert

if(@Action=3) -- Only Update
begin
IF (UPDATE([First_Name]) OR UPDATE([Last_Name]) OR UPDATE([JobCode]) OR UPDATE([Inactive]))
Begin

End
end

if(@Action=2) -- Only Delete
KumarHarsh
la source