fr en

Accéder aux données avec SQL pltôt qu'en RPG natif

Bonne pratique :

Il est conseillé de remplacer, dans les programmes RPG, les accès aux données natifs (SETLL/CHAIN/READ/DELETE/UPDATE/…) par des requêtes SQL.

Le langage RPG a évolué et permet ,depuis déjà longtemps, d’intégrer des requêtes SQL dans le code RPG : c’est le SQL EMBEDDED.

Pourquoi :

Les requêtes SQL offrent les avantages suivants :

  • Elles peuvent être plus performantes lorsqu’elles sont bien écrites.

  • Elles peuvent par l’utilisation des jointures ou des CTE permettre la lecture de plusieurs tables en une seule requête.

  • Elles peuvent bénéficier des fonctions SQL qui permettent de traiter les données dés la lecture. Ce qui offre de nombreuses possibilités (sélections, transformations, contrôles, agrégations) très intéressantes et très puissantes.

  • Elles peuvent facilement, si le besoin est, être décorrélées du code RPG et donc exportées.

  • Elles sont compréhensibles par des personnes qui ne maîtrisent pas le langage RPG.

Remarque :

Attention : Faire du SQL EMBEDDED ne se résume pas à :

  • Remplacer une boucle de lecture native par un curseur SQL
  • Ou juste remplacer un CHAIN par un SELECT SQL.

Il faut optimiser les requêtes SQL.
En effet, si on se contente de remplacer chaque accès aux données RPG par une requête SQL équivalente, il y a de fortes chances pour que les temps de traitements se dégradent.
Pour gagner en temps de traitement, il faut impérativement utiliser les capacités offertes par SQL.
Et là, les gains de performances peuvent être très importants.

Voici, ci-dessous, un exemple montrant combien il est important de bien optimiser les requêtes SQL dans le SQL EMBEDDED.

Exemple :

Ces exemples reposent sur 2 tables :

  • TB_LIGNES : table des lignes de commandes. Cette table contient 4 champs :
    • COMMANDE_ID : Identifiant de la commande
    • LIGNE_ID : numéro de ligne de commande
    • ARTICLE_ID : code article
    • QUANTITE : quantité commandée
  • TB_ARTICLES : tables des Articles. Cette table contient 2 champs :
    • ARTICLE_ID : code Article
    • PRIX_ARTICLE : prix de l’article

La table TB_ARTICLES contient 100 articles avec des prix différents compris entre 0 et 99.

La table TB_LIGNES contient 273935 lignes de commandes.

Les 3 programmes suivant vont devoir lire chaque ligne de commande de la table TB_LIGNES, et en fonction de l’article et de la quantité lus, calculer la valeur de la ligne de commande en récupérant le prix de l’article dans la table TB_ARTICLES afin de calculer la valeur total des lignes de commande.

Le programme N°1 accède aux données en RPG natif selon le principe suivant :

TOTAL = *ZEROS;

// Boucle de lecture de toutes les lignes de TB_LIGNES
SETLL *LOVAL TB_LIGNES;
READ TB_LIGNES;
// Tant que pas fin de TB_LIGNES
DOW NOT %EOF(TB_LIGNES);
      // Pour chaque ligne lue dans TB_LIGNES, lecture directe dans la table ARTICLES
      CHAIN (ARTICLE_ID) TB_ARTICLES;
      IF %FOUND(TB_LIGNES);
          TOTAL = TOTAL + (QUANTITE * PRIX);
      ENDIF;
      // Lecture de la ligne suivante de TB_LIGNES
      READ TB_LIGNES;
ENDDO;

Le programme N°2 accède aux données en SQL non optimisé selon le principe suivant :

TOTAL = *ZEROS;

// Définition d’un curseur pour lire toutes les lignes de TB_LIGNES
EXEC SQL
      DECLARE CURS_NON_OPTIMISE CURSOR FOR
      SELECT ARTICLE_ID, QUANTITE
      FROM TB_LIGNES;

// Ouverture du curseur
EXEC SQL
OPEN CURS_NON_OPTIMISE;

EXEC SQL
FETCH CURS_NON_OPTIMISE
INTO :l_ARTICLE_ID, :l_QUANTITE;

// Boucle de lecture des lignes de TB_LIGNES
DOW SQLCODE = 0;
      // à chaque ligne lue dans TB_LIGNES, recherche du prix dans TB_ARTICLES
      EXEC SQL
            SELECT ARTICLE_PRIX
            INTO :l_PRIX
            FROM TB_ARTICLES
            WHERE ARTICLE_ID = :l_ARTICLE_ID;

      IF SQLCODE = 0;
          TOTAL = TOTAL + (l_QUANTITE * l_PRIX);
      Endif;

      // Lecture ligne suivante de TB_LIGNES
      EXEC SQL
      FETCH CURS_NON_OPTIMISE
      INTO :l_ARTICLE_ID, :l_QUANTITE;
ENDDO;

//  Fermeture du curseur
EXEC SQL
CLOSE CURS_NON_OPTIMISE;

Le programme N°3 accède aux données en SQL optimisé selon le principe suivant :

TOTAL = *ZEROS;

// Jointure sur TB_LIGNES et TB_ARTICLES
// et somme des montants calculés sur chaque ligne jointe
EXEC SQL
     SELECT SUM(B.ARTICLE_PRIX * A.QUANTITE)
     INTO :TOTAL
     FROM          TB_LIGNES    A
     INNER JOIN TB_ARTICLE B
     ON B.ARTICLE_ID = A.ARTICLE_ID;

Les 3 programmes ont été exécutés plusieurs fois simultanément et voici les temps de traitement observés pour chacun d’eux :

Conclusions : 

  • Les 3 programmes donnent le même résultat (colonne TOTAL CALCULE identiques) mais les temps de traitement diffèrent en fonction du type d’accès aux données utilisé.
  • Le SQL optimisé est bien plus performant que les autres mode d’accès aux données.
  • Le SQL non optimisé est moins performant que les accès en RPG natif.

Faire du SQL dans les programmes RPG : c’est bien à condition d’optimiser les requêtes SQL EMBEDDED sans quoi les performances risquent de s’en ressentir.