Vous consultez une page technique concernant le logiciel de gestion Mercator. Celle-ci contient des informations spécifiques destinées aux professionnels de Mercator. Souhaitez-vous être redirigés vers des informations plus générales ?


   Ne plus poser cette question

Ecriture optimale des triggers

0000003024     -      17/01/2022

L'utilisation de triggers à mauvais escient ou dans de mauvaises conditions peut altérer sérieusement les performances du serveur SQL et donc de Mercator.
De manière générale, il faut faire très attention à la mise en place d’un trigger dans un dossier.

Voici donc quelques règles à respecter lors de l’élaboration de triggers "customs" :

  • Il ne faut jamais modifier un trigger standard, celui-ci est susceptible d’être réinstallé par Mercator lors d’une mise à jour.

  • Un trigger peut concerner plusieurs enregistrements. Il faut donc toujours travailler avec les tables inserted/deleted, jamais avec des variables scalaires.
    Exemple :
    update stock
            set stock.s_cle3 = inserted.s_condit_a
            from stock inner join inserted on inserted.s_id = stock.s_id


  • En standard, il peut y avoir un trigger pour chaque opération (insert, update, delete). S'il s'avère nécessaire d'ajouter d'autres triggers, il faut regrouper ceux-ci pour n’avoir qu’un seul (au maximum) par opération.
    L'idéal est de, si possible, regrouper les différentes opérations en un seul trigger; notamment les triggers "on insert" et "on update". Pour savoir si vous êtes en modification, il faut tester qu'il y ait un enregistrement dans la table deleted.
    Exemple :
    if update(s_cle2) and exists(select * from deleted)
           begin
                 IF @@TRANCOUNT > 0 ROLLBACK TRAN
                 RAISERROR ( 'Stock.s_cle2 ne peut être modifié!',16,1 )
                 return
    end


  • Il faut, au maximum, utiliser les syntaxes "if update(nomduchamp)" pour n'exécuter ce code que si le champ est réellement modifié (même en cas d’insert).
    Ce principe n'est pas utilisable pour le delete.
    Exemple :
    if update(s_cle2)
    Ceci permet également d'éviter la récursivité des triggers. Cela peut se produire quand les champs (table STOCK, par exemple) mis à jour par un trigger, déclenchent eux-mêmes un autre trigger sur cette même table (qui pourrait mettre à jour un des champs gérés par le premier trigger). Une boucle infinie est alors générée.

  • Vu l’importance d’un trigger par rapport aux performances, il ne faut pas chercher à faire un "beau" trigger bien lisible, mais il faut, au maximum, optimiser celui-ci pour avoir le moins d’impact possible sur les performances.
    Exemple :
    if update(s_taux_tva)
    begin        update stock               set stock.s_comptev = case when inserted.s_compte_manuel = 1 then stock.s_comptev when inserted.s_articlenormal = 0 then '700000' else  '700210' end,               stock.s_comptea = case when inserted.s_compte_manuel = 1 then stock.s_comptev when inserted.s_articlenormal = 0 then '604000' else '604210' end        from stock inner join inserted on inserted.s_id = stock.s_id end if update(s_type_etiq) begin        update stock               set stock.s_etiq = case when stock.s_type_etiq = 'CARTON' then '3' when  stock.s_type_etiq = 'COLLANTE' then '2' else '1' end        from stock inner join inserted on inserted.s_id = stock.s_id end if update(s_condit_a) begin        update stock               set stock.s_cle3 = inserted.s_condit_a        from stock inner join inserted on inserted.s_id = stock.s_id end
    devient :
    if update(s_taux_tva) or update(s_type_etiq) or update(s_condit_a)
    begin
           update stock
           set stock.s_comptev = case when inserted.s_compte_manuel = 1 then stock.s_comptev when inserted.s_articlenormal = 0 then '700000' else  '700210' end,
                  stock.s_comptea = case when inserted.s_compte_manuel = 1 then stock.s_comptev when inserted. articlenormal = 0 then '604000' else '604210' end,
                 stock.s_etiq = case when stock.s_type_etiq = 'CARTON' then '3' when  stock.s_type_etiq = 'COLLANTE' then '2' else '1' end,
                 stock.s_cle3 = inserted.s_condit_a
           from stock inner join inserted on inserted.s_id = stock.s_id
    end
    
    Au lieu de 3 updates sur la table stock, nous n’avons plus qu’un seul update pour l’ensemble des champs concernés. Ceci va permettre d'éviter des appels successifs du trigger.

    On pourrait aller plus loin en faisant ces tests en plus et en adaptant la requête avec les champs concernés (pour éviter les "case when ... then ... else ... end") :
    if update(s_taux_tva) and update(s_type_etiq) and update(s_condit_a) ...
    if update(s_taux_tva) and update(s_type_etiq) and not update(s_condit_a) ...
    if update(s_taux_tva) and not update(s_type_etiq) and update(s_condit_a) ...
    if not update(s_taux_tva) and update(s_type_etiq) and update(s_condit_a) ...
    if update(s_taux_tva) and not update(s_type_etiq) and not update(s_condit_a) ...
    if not update(s_taux_tva) and update(s_type_etiq) and not update(s_condit_a) ...
    if not update(s_taux_tva) and not update(s_type_etiq) and update(s_condit_a) ...

 


 

Spécificités de la table DISPO
  • Il arrive fréquemment que le trigger est écrit comme si toutes les lignes (donc tous les dépôts) d’un article mouvementé se trouvent dans inserted. On voit donc souvent, par exemple :
    sum(dispo-qv_3) from inserted
    Ceci est incorrect : Inserted contient uniquement la/les ligne(s) modifiée(s) ; donc, classiquement, lors de la validation d’un document de la gestion commerciale, une seule ligne correspondant au dépôt mouvementé.

  • De même, une erreur fréquente est de mettre un trigger sur DISPO qui alimente un champ de la table STOCK sans tester l’ID du dépôt. Cela fonctionne tant que le dossier n’a qu’un seul dépôt. Mais le jour où un second dépôt est défini, on reçoit donc dans la table STOCK, aléatoirement, la valeur du dépôt 1 ou la valeur du dépôt 2, selon le dernier dépôt mouvementé.
    Donc, même pour un dossier avec un seul dépôt, il faut tester dans inserted le dépôt pour lequel la valeur doit être reportée dans la table STOCK.