Another blog



SQL 2005: Obtenir les valeurs avant et aprés modification dans un trigger

Publié le 28/05/2008 par Another

Imaginons que vous ayez une table "Produits", et que pour des raisons diverses votre base de données étant dénormalisé, vous souhaitez mettre à jour la table "Produits_Histo" dés lors que vous modifier la colonne "intituleProduit". Je dis bien imaginons car de manière générale je ne vous encourage pas à développer ce genre de solution, beaucoup de gens vous dirons que ce n'est pas bien, moi le premier, mais faut parfois faire ce qu'on peut avec ce qu'on a, alors me soyons pas trop con-descendant , et regardons ensemble une solution implémentable dans le monde réel.

Oracle Trigger vs SQL Server Trigger

 Oracle permet de créer des Triggers FOR EACH ROW, dans lesquels vous déclarer les références : NEW et :OLD afin de pouvoir accéder au valeur de la ligne avant la modification et après la modification.

 Un trigger FOR EACH ROW s'exécute pour chacune des lignes modifiées, nous pourrions alors facilement mettre en place notre solution dans un trigger de ce type en utilisant le code suivant :

 If :NEW. intituleProduit <> :OLD. intituleProduit Then

  update Produits_Histo set intituleProduit = :NEW. intituleProduit

End If

 SQL Server en revanche ne gère le Trigger FOR UPDATE que de manière ensembliste, on n'a pas la granularité ligne à ligne de Oracle.

 SQL Server Trigger FOR UPDATE

 Comme je vous le disais précédemment, un Trigger FOR UPDATE sous SQL Server est ensembliste, c'est à dire, que le trigger va s'exécuter qu'une fois lorsque tous les enregistrements auront étés mis à jour.

 Dans un trigger, SQL Server permet d'accéder á deux pseudo-table nommé DELETED  et INSERTED qui contienne respectivement les données avant UPDATE et les données après UPDATE.

 Pour obtenir le même résultat que dans le chapite précédent, il faut créer un curseur reliant les deux tables, le parcourir avec FETCH et procéder a la mise à jour des enregistrements dans la boucle de lecture.

 Pour exemple :

CREATE TRIGGER dbo.TRG_AU_PRODUITS
   ON  dbo.Produits
   AFTER UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    DECLARE @productId int, @newName varchar(50)

    DECLARE updateCursor CURSOR FOR 
    SELECT Inserted.customerId,
           Inserted.productName AS afterName,
           Deleted .productName AS beforeName
      FROM Inserted,
           Deleted 
     WHERE Inserted.productId = DEL.productId
       AND Inserted.productName <> Deleted.productName

    OPEN updateCursor
    FETCH NEXT FROM updateCursor INTO @productId, @newName

    IF @@FETCH_STATUS <> 0 
      RETURN

    WHILE @@FETCH_STATUS = 0
    BEGIN
      UPDATE dbo.Products_Histo 
         SET productName = @newName
       WHERE productId = productId
         
      FETCH NEXT FROM updateCursor INTO @productId, @newName
    END

   CLOSE updateCursor
   DEALLOCATE updateCursor
END
GO