Un Cold Case, le Decode

Oubliez le Decode !

Quel est le souci ?!?

      Il faut parfois avoir un petit côté agent secret pour comprendre le code SQL. Cela prend tout son sens avec la bien nommée fonction "decode" fournie généreusement par Oracle.
Que fait cette fonction ?
decode(expression, valeur1, résultat1 [, valeurN, résultatN...] [, résultat par défaut])
Elle compare "expression" à "valeur1" et si ça concorde la fonction renvoie "résultat1". Si cela ne concorde pas "expression" est comparée avec "valeur2", si ça concorde on obtient "résultat2" etc. etc. jusqu’à renvoyer un éventuel "résultat par défaut"
       Cette fonction peut avantageusement être remplacée avec une syntaxe CASE. Le CASE est largement répandu dans les langages de programmation, de script etc. SQL Server, PostgreSQL, Oracle etc. acceptent bien sûr sans problème le CASE donc je vais essayer de vous convaincre d’abandonner DECODE, même avec Oracle.
Supposons que vous disposiez d’une table t avec une seule colonne x et dans cette table une seule ligne ayant x=3. Que renvoient ces requêtes ?

select CASE WHEN x = 1 or x = 2 THEN 1 WHEN x = 3 THEN 2 WHEN x = 4 or x = 5 THEN 3 ELSE 0 END from t; select decode(x,1,1,2,1,3,2,4,3,5,3,0) from t;

       Bon d’accord l’avantage du CASE n’est pas flagrant. On obtient 2 et, dans les deux cas, c’est assez lisible. Sur des exemples simples la syntaxe Oracle n’est donc pas trop difficile à décoder. Le problème c’est que les habitudes se prennent vite et que vous allez peut-être vouloir l’utiliser sur des cas plus compliqués.

L’affaire se corse...

       Pas convaincu ? A votre tour de jouer. Vous avez toujours votre table t avec sa colonne x. Ecrivez avec la fonction DECODE une requête qui renvoie la chaîne ERREUR si x < 0, OK si x = 0 (le cas le plus fréquent) et WARNING si x > 0. Avec un CASE ça peut donner :

select CASE WHEN x = 0 THEN 'OK' WHEN x < 0 THEN 'ERREUR' WHEN x > 0 THEN 'WARNING' END from t;

      Alors vous avez trouvé ? Je suppose que oui mais c’est déjà moins lisible je trouve. Julien m’a proposé :
select decode(sign(x),0, 'OK', -1, 'ERREUR',1, 'WARNING') from t;

      Dans la même veine supposons que vous ayez une table perso (nom varchar(128) , date_naissance timestamp)
Ecrivez, sachant que la majorité en France est fixée à 18 ans, une requête avec DECODE affichant le nom et la chaîne :
      Avec PostgreSQL et un CASE je propose :
select nom, case when age(date_naissance) >= interval '18 years' then 'MAJEUR' else 'MINEUR' end from perso;

      Alors facile ? Avec Oracle et DECODE on me murmure que ça pourrait donner :
select nom, decode(sign(months_between(sysdate, date_naissance)-216), -1, 'MINEUR', 'MAJEUR') from perso;

      Quelle est la syntaxe la plus claire ? Je ne sais pas pour vous mais je ne suis pas développeur et la syntaxe Oracle commence à devenir difficile à comprendre avec mes moyens limités.

Toujours plus difficile...

      Supposons que vous ayez une table avec les matricules des employés de la boîte et les salaires.
PostgreSQL : perso(matricule integer, salaire double precision)
Oracle : perso(matricule number, salaire number)

      Vous apprenez que la Gauche pourrait gagner les élections. Vous allez à présent écrire une requête qui renvoie le matricule de l'employé, son salaire et

      Pour cet exemple vous pouvez utiliser une valeur fixe de 1500 pour le SMIC. Je propose avec PostgreSQL :
with plafond as (select 20*min(salaire) salaire from perso where salaire >= 1500) select perso.matricule, perso.salaire, CASE WHEN perso.salaire >= 1500 and perso.salaire <= (select salaire from plafond) THEN 'NORMAL' WHEN perso.salaire < 1500 THEN 'ANOMALIE CRITIQUE' WHEN perso.salaire > (select salaire from plafond) THEN 'ANOMALIE SERIEUSE' WHEN perso.salaire is null THEN 'SALAIRE NON RENSEIGNE' END from perso;

      Vincent me propose cette version avec un DECODE sous Oracle :
select s.matricule, s.salaire, decode(s.salaire, null, 'SALAIRE NON RENSEIGNE', decode(sign(s.salaire-1500), -1, 'ANOMALIE CRITIQUE', decode(sign(s.salaire-bs.sal_min*20), 1, 'ANOMALIE SERIEUSE', 'NORMAL'))) from (select min(salaire) as sal_min from perso where salaire >= 1500) bs, perso s;

      Certes la version avec DECODE a moins de lignes mais j’aime bien que la requête soit compréhensible à la première lecture. Pour moi l’écriture en CASE est la plus claire (attention claire ou pas claire ça ne dispense pas de documenter bien sûr !) Je fixe un plafond bien visible avant de commencer (WITH) et ensuite chaque condition est explicitement examinée. On obtient NORMAL si le salaire est entre le SMIC et ce plafond, ANOMALIE CRITIQUE si le salaire est inférieur au SMIC etc.
      N’hésitez pas à donner votre avis sur le forum !

Mise à jour : 25/11/2016