jeudi 28 mars 2013

Horizontalisation SQL

Bonjour,

Je présente ici une fonction permettant l'horizontalisation d'une requête SQL ou le déformatage de celle-ci, si vous préférez. Elle peut être également utiliser pour autre chose que du script SQL.

Quand je code, j'utilise les techniques en adéquation lié au projet. Soit je prend un ORM ou bien j'utilise du code texte avec des fonctions d'échappements (en l’occurrence SQL) pour les autres occasions. Si vous utilisez un ORM, nul besoin d'utiliser cette fonction.

Par contre si vous avez l'habitude de saisir vos requêtes verticalement dans votre code source pour une bonne lecture de celle-ci et que vous considérez (névrotiquement ou non) que transmettre tel quel la requête au serveur est un sacrilège en soit, vous pouvez donc prévoir l'emploi de cette fonction.

Ceci dit, j'aurais peut être réussi à la faire en tant qu'expression régulière, mais je n'ai pas pris la peine de m'y attardé pour la raison suivante.
Même si je considère les expressions régulières par principe génialissime, elles sont dangereuses pour la compréhension du code (non commenté), mais également pour une maintenance à long terme.
Il existe beaucoup de champions d'expressions régulières, mais souvent la lecture d'une expression peut prendre du temps et être mal interprété. D'ou l'existence, quasi obligatoire, de tests unitaires. Ne serait ce que pour les comprendre après une période d'abstinence dans la lecture de codes sources.

Pour utiliser cette fonction il suffit de transmettre la chaîne de votre requête SQL (échappé, je l'espère) en paramètre et celle-ci vous la retournera sans les éléments définis par défaut dans la fonction (les retours à la ligne, saut de lignes, tabulations et double espace). Les éléments n'étant retirés que de la partie d'instruction et non des valeurs transmises. J'ai rajouté le paramètre $elements pour surcharger la fonction si nécessaire.

Comme actuellement je travail beaucoup en PHP, vous trouverez le code sous cette forme.
La transformation vers un autre langage est à mon avis très aisé. Dans cet exemple, je n'ai pas pris en compte le traitement de chaînes multi-octets (ex: utf8). Je vous laisserais le plaisir de le faire.
 /**  
  * @author zenerzhul  
  * @name  horizontalisation  
  * @version 0.4  
  * @param  string $chaine  
  * @param  array $element (optional) 
  * @return string  
  */  
 function horizontalisation( $chaine, $elements = NULL )  
 {  
   if ( is_null( $elements ) )  
   {  
    $elements = array( chr( 9 ), chr( 10 ), chr( 13 ), str_repeat( chr( 32 ), 2 ) );  
   }  
   foreach ( $elements as $recherche )  
   {  
    $decalage = 0;  
    $largeur = strlen( $recherche );  
    $i = strpos( $chaine, $recherche, $decalage );  
    while ( $i !== FALSE )  
    {  
      if ( !( strpos( $chaine, "'" ) < $i ) ^ !( strpos( $chaine, "'", $i + 1 ) > $i ) )  
      {  
       $chaine = substr( $chaine, 0, $i ) . ' ' . substr( $chaine, $i + $largeur );  
       $decalage = $i;  
      }  
      else  
      {  
       $decalage = $i + $largeur;  
      }  
      $i = strpos( $chaine, $recherche, $decalage );  
    }  
   }  
   return $chaine;  
 }

Voici un example d'utilisation (MySQL) :
 $string = "SELECT  
        boson,  
        lepton,  
        fermion  
       FROM   
        spectral   
       WHERE   
         fi = 1  
        AND  
         alpha <> 'B''  
       E T      
       A'  
       LIMIT 0 ,   10;";  
 echo '<pre>' . horizontalisation( $string ) . '</pre>';  

Résultat :
 SELECT boson, lepton, fermion FROM spectral WHERE fi = 1 AND alpha <> 'B''
            E T       
            A' LIMIT 0 , 10;  

Si vous comprenez le résultat ci-dessus, très bien et c'est normal. Maintenant pour ceux qui s’attendaient à autre chose, laissez-moi vous expliquer.

Premièrement la chaîne $string est une chaîne de caractères avec des retours à la ligne, des tabulations et ou des espaces multiples, bien !

Dans l'exemple je demande à afficher le résultat à travers une balise <pre>, ce qui a pour objectif de traiter le texte dans son intégralité et non d'absorber les caractères spécifiques (retour à la ligne, saut de page, tabulation, espace multiple) en un seul et même caractère, j'ai nommé, l'espace. Donc nous nous retrouverons comme si nous allions avoir un résultat brute.

Toujours dans l'exemple, l'appel à la fonction horizontalisation ne transmet que la chaîne $string, car nous n'avons pas besoin de surcharger la variable $elements dans notre cas. De toute façon, cette variable $elements est contrôlé puis définie si rien n'a été transmit lors de l'appel à la fonction.

À l'intérieur de cette fonction nous allons traiter les $elements 1 par 1.  Je ne suis pas certains que ce soit le meilleur algorithme, mais bon pour faire trois cycle, vous voyez...

Dans la boucle secondaire while ( ... traite la chaîne $chaine tant que le caractère recherché est trouvé. De plus, au coeur de cette boucle nous vérifions bien que le caractère recherché à remplacer par un seul espace n'est pas compris entre des guillements (single quote) avec le test conditionnel suivant :
 if ( !( strpos( $chaine, "'" ) < $i ) ^ !( strpos( $chaine, "'", $i + 1 ) > $i ) )  

Le test nous dit que le caractère ne peux se trouver que soit avant un guillemet soit après mais pas au centre. Pour info le caractère ^ est un xor (ou exclusif).

Donc voila, c'est tout.

PS : J'ai cherché cette fonction sur le web sur différent site de programmation, je ne l'ai pas trouvé. Elle ne fait pas grand chose mais elle le fait. Et surtout elle ne fait que ce pourquoi elle est censé exister.