Aller au menu - Aller au contenu

Icône Les conditions

Mise à jour : 29/06/2009
1 063 visites depuis 7 jours, dont 72 sur ce chapitre classé 122/786
Les conditions SQL ne sont pas forcément importantes pour l'optimisation mais sont extrêmement pratiques pour obtenir des données tirées d'autres données très rapidement. Elles permettent de renvoyer une valeur si une expression est vraie, ou une autre valeur si elle est fausse...

Enfin, je vous laisse découvrir ça, vous vous rendrez vite compte de leur utilité. ;) :)
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Des conditions en SQL ? Différences entre MySQL et la norme

La norme SQL prévoit ce qu'on appelle des structures conditionnelles qui permettent de dire "SI telle valeur vaut ça, faire ça, sinon si cette valeur vaut ça, faire ça, sinon faire ça".

Ce n'est bien sûr qu'un exemple, mais c'est le principe. Une structure conditionnelle permet d'énoncer... Des conditions. :p

Dans MySQL, il existe 4 structures conditionnelles :

  • CASE
  • NULLIF
  • IF
  • IFNULL


Les deux dernières structures peuvent très facilement être obtenues à partir des deux premières et sont plus limitées. En fait, elles sont spécifiques à MySQL et n'existent pas dans la norme.

Il est notamment déconseillé d'utiliser IF car si un jour vous passez à PostGreSQL (par exemple), vous allez devoir restructurer toutes vos requêtes... :(

IFNULL, même s'il n'est pas repris par la norme, est implémenté dans la plupart des SGBDR (notamment MySQL, PostGreSQL et SQL Server), cela est donc moins grave si vous l'utilisez, mais c'est déconseillé quand même...

Utilisation de CASE

CASE est la structure conditionnelle la plus répandue parmis les SGBDR. Et c'est également la plus complète ! Les autres structures sont toutes dérivées de celle-ci.
Pour toutes les structures conditionnelles suivantes, je vous montrerais à chaque fois le rapport entre la structure et CASE.

Bon alors comment on l'utilise, ce CASE ?


C'est assez simple, voici tout d'abord une structure générale :

Code : SQL
1
2
3
4
5
6
CASE [colonne]
WHEN condition THEN valeur
WHEN condition THEN valeur
[...]
ELSE valeur
END


Bon, peut-être qu'à première vue, comme ça, ça ne vous parle pas trop, mais avec les exemples ça rentrera tout seul. ;) :)

Faites attention à ne pas oublier le END à la fin du CASE, sinon vous aurez droit à une belle erreur d'exécution ! :colere2:


Voici un premier exemple :

Code : SQL
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
SELECT @a, CASE
        WHEN @a > 0 THEN 'positif'
        WHEN @a < 0 THEN 'negatif'
        WHEN @a = 0 THEN 'nul'
        ELSE 'NULL'
        END AS signe_a;
SET @a = -5;
SELECT @a, CASE
        WHEN @a > 0 THEN 'positif'
        WHEN @a < 0 THEN 'negatif'
        WHEN @a = 0 THEN 'nul'
        ELSE 'NULL'
        END AS signe_a;

Image utilisateur


Je crois que l'exemple se passe de commentaire : le code veut dire ce qu'il veut dire. :p

Imaginons maintenant un autre cas : on fait un script de news et on a une colonne dans la table des news qui s'appelle 'validee' et qui indique si la news a été validée par un admin ou pas.

Dans le panel d'administration, on affiche toutes les news dans un tableau, et dans la colonne "Validée ?", on aimerait indiquer "validée", ou "en attente de validation" plutôt que 0 ou 1...

Rien de plus simple avec CASE ! Voici une table fictive :

Code : SQL
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
CREATE TABLE news (
id int NOT NULL AUTO_INCREMENT,
titre varchar(100) NOT NULL,
validee tinyint NOT NULL DEFAULT 0,
PRIMARY KEY(id)
);
 
INSERT INTO news (titre, validee) VALUES ('news 1', 1);
INSERT INTO news (titre, validee) VALUES ('news 2', 0);
INSERT INTO news (titre, validee) VALUES ('news 3', 0);
INSERT INTO news (titre, validee) VALUES ('news 4', 2);
INSERT INTO news (titre, validee) VALUES ('news 5', 1);
INSERT INTO news (titre, validee) VALUES ('news 6', 0);


Essayez de trouver une requête qui renverra :
  • L'id de la news
  • Le titre de la news
  • Si la colonne validee vaut 1, 'Validee', si la colonne vaut 0, 'En attente de validation', sinon 'Etat inconnu' dans une colonne nommée validee_txt


Quand vous penserez avoir trouvé la requête, comparez votre code au mien :

Secret (cliquez pour afficher)
Code : SQL
1
SELECT id, titre, CASE WHEN validee = 1 THEN 'Validee' WHEN validee = 0 THEN 'En attente de validation' ELSE 'Etat inconnu' END AS validee_txt FROM news;

Image utilisateur


Bon, normalement c'était pas trop dur. :D

Oui mais attend, tantôt en nous montrant la structure de base, j'ai vu qu'elle commençait comme ça : CASE [colonne] WHEN ... A quoi correspond ce [colonne] ?


Bien vu, en fait l'exemple que je viens de vous montrer peut être simplifié grâce à ce [colonne]. Comme un exemple vaut mieux qu'un long discours, voici le code qu'on aurait pu aussi utiliser :

Code : SQL
1
SELECT id, titre, CASE validee WHEN 1 THEN 'Validee' WHEN 0 THEN 'En attente de validation' ELSE 'Etat inconnu' END AS validee_txt FROM news;

Image utilisateur


Pratique aussi non ? Le seul problème avec cette syntaxe c'est qu'elle ne s'applique pas à tous les cas, par exemple notre premier exemple (savoir si @a était positif, négatif, nul (0) ou NULL) ne pourrait utiliser cette syntaxe car on ne peut pas mettre le signe ">" ou "<".

Vous suivez toujours ? Tant mieux parce qu'on a fait le plus dur. :D :p

Les autres structures conditionnelles

Comme je vous l'ai dit, il existe trois autres structures conditionnelles en dehors de CASE en MySQL : IF, NULLIF et IFNULL.

Nous avons déjà vu l'utilité de IFNULL mais je vais en reparler juste pour vous montrer le lien avec CASE et la facilité de ce dernier.

Une alternative simplifiée : IF



IF est en fait une fonction. Voici comment on l'utilise :

Code : SQL
1
SELECT IF(condition, operation_si_vrai, operation_si_faux);


Comme vous le constatez, IF est beaucoup plus limité que CASE, voici comment on pourrait reproduire IF dans un CASE :

Code : SQL
1
SELECT CASE WHEN condition THEN operation_si_vrai ELSE operation_si_faux END;


Vous vous souvenez de ce qu'on a vu au chapitre précédent ? La table ex_null qui contenait des scores ou NULL si le joueur n'avait joué aucune partie...

Et bien le IF se prête bien à cette situation, nous avions utilisé cette requête :

Code : SQL
1
SELECT id, COALESCE(IFNULL(points, 0) / COALESCE(points, 1), 1) AS a_deja_joue FROM ex_null;

Image utilisateur


Et bien avec le IF, voici ce qui suffirait :

Code : SQL
1
SELECT id, IF(points IS NULL, 0, 1) AS a_deja_joue FROM ex_null;


On peut même l'améliorer en mettant oui / non à la place de 1 / 0. :)

Code : SQL
1
SELECT id, IF(points IS NULL, 'non', 'oui') AS a_deja_joue FROM ex_null;

Image utilisateur


Comme vous le voyez, c'est beaucoup plus pratique et plus simple que notre solution à base de COALESCE / IFNULL. :D :p

NULLIF



Code : SQL
1
SELECT NULLIF(expr1, expr2) AS resultat;


SI expr1 = expr2 ALORS resultat vaudra NULL
SINON resultat vaudra expr1.

Avec CASE :

Code : SQL
1
SELECT CASE WHEN expr1 = expr2 THEN NULL ELSE expr1 END AS resultat;


Bon, en fait l'utilité de cette fonction est assez... réduite. :-° Rarissimes sont les cas où elle vous servira, donc je ne détaille pas. ^^

IFNULL



On a déjà vu l'utilité de cette fonction, qui s'utilise comme cela :

Code : SQL
1
SELECT IFNULL(expr1, expr2) AS resultat;


SI expr1 est NULL ALORS renvoyer expr2
SINON renvoyer expr1

Avec CASE :

Code : SQL
1
SELECT CASE WHEN expr1 IS NULL THEN expr2 ELSE expr1 END AS resultat;


Personnellement, je trouve cette fonction tout autant inutile que la précédente, dans la plupart des cas, un CASE (voir même un IF, comme nous l'avons vu) fait parfaitement l'affaire !

Si j'étais vous, je n'encombrerais pas mon cerveau des deux dernières fonctions (NULLIF et IFNULL)...

J'ai dit que le IF était à éviter pour prévenir les ennuis lors d'un changement de SGBDR, toutefois la plupart de ces derniers supportent les fonctions utilisateur, qui permettent de recréer une fonction IF sans encombre, ne vous privez donc pas du IF s'il peut vraiment vous être utile, mais évitez absolument les imbrications de IF et préférez le CASE, créé pour gérer le ELSEIF. :)

[Problème SQL] Somme des nombres si leur nombre correspond à un nombre...

Voici le premier problème SQL que je vous propose, d'autres suivront, en fait un problème SQL ressemble à un mini-TP sauf qu'un mini-TP vous servira peut-être un jour, tandis que les problèmes sont totalement loufoques et ne vous serviront certainement jamais. :D

J'espère que vous trouvez le titre accrocheur. o_O

Voici le problème : on a une table de 7 colonnes dont une "id" :

Code : SQL
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
CREATE TABLE probleme1 (
        id int NOT NULL AUTO_INCREMENT,
        i1 int DEFAULT NULL,
        i2 int DEFAULT NULL,
        i3 int DEFAULT NULL,
        i4 int DEFAULT NULL,
        i5 int DEFAULT NULL,
        i6 int DEFAULT NULL,
        PRIMARY KEY(id)
);
 
INSERT INTO probleme1 (i1, i2, i3, i4, i5, i6) VALUES
(NULL, NULL, 5, NULL, 4, 3),
(3, NULL, NULL, 8, NULL, NULL),
(NULL, NULL, NULL, NULL, 5, 6),
(NULL, NULL, NULL, NULL, NULL, NULL),
(8, -4, 3, NULL, NULL, NULL),
(1, 1, 1, 1, 1, 1),
(9, NULL, 2, NULL, NULL, 0),
(-8, 2, -1, NULL, 4, 1),
(-1, 1, -1, 1, -1, 1),
(0, 0, 0, NULL, NULL, 0);


Rappel :
  • 1 + NULL = NULL
  • 0 OR NULL = NULL
  • 1 OR NULL = 1


On veut obtenir :
  • Une colonne id contenant simplement l'id de la ligne.
  • Une colonne somme contenant la somme des colonnes i1, i2, i3, i4, i5 et i6 ou NULL lorsque toutes ces colonnes valent NULL.
  • Une colonne nb_cols qui contiendra le nombre de colonnes non-nulles.
  • Les colonnes i1 à i6.


Il est possible de créer la colonne nb_cols sans aucune condition. :D


Voici ce que vous devriez arriver à obtenir :
Image utilisateur


Je sais très bien que ce que je vous demande est loin d'être facile, mais en SQL, rares seront les cas où tout sera facile et viendra du premier coup ! Croyez moi ce problème est un bon entrainement, essayez de le faire sérieusement et ne vous découragez pas, si vous n'y arrivez pas ne regardez pas la solution ! Demander un indice sur le forum PHP / MySQL serait une bien meilleure idée !


Secret (cliquez pour afficher)
Code : SQL
1
2
3
4
5
6
7
SELECT
        id, @somme := CASE
                WHEN (((((i1 + 1 OR i2 + 1) OR i3 + 1) OR i4 + 1) OR i5 + 1) OR i6 + 1) IS NULL THEN 'NULL'
                ELSE COALESCE(i1, 0) + COALESCE(i2, 0) + COALESCE(i3, 0) + COALESCE(i4, 0) + COALESCE(i5, 0) + COALESCE(i6, 0) END AS somme,
        6 + @somme - COALESCE(i1, 1) - COALESCE(i2, 1) - COALESCE(i3, 1) - COALESCE(i4, 1) - COALESCE(i5, 1) - COALESCE(i6, 1) AS nb_cols,
        i1, i2, i3, i4, i5, i6
FROM probleme1;

:waw:

Bien sûr, il serait tout à fait surprenant que vous ayez trouvé exactement le même que moi, mais ce qui compte pour le moment, c'est que ça fonctionne, pas que ce soit optimisé et supersonique ! :p Ma requête est de votre niveau, mais sachez qu'il existe d'autres façons de faire encore plus rapides et pratiques ! Mais notre niveau n'est pas encore assez élevé pour ce type de requêtes et on s'en tiendra à la mienne pour le moment. :p
Si vous n'avez pas la même requête que moi c'est normal, mais essayez de comprendre la mienne, c'est important !
Image utilisateur

Comme vous le constatez, les structures et fonctions conditionnelles de MySQL permettent d'éviter facilement du traitement du côté de PHP. Toutefois, il ne faut pas non plus utiliser les conditions pour tout et n'importe quoi, certains traitements sont plus rapides du côté de PHP que du côté de MySQL, mais en général le mieux est de les faire du côté de SQL qui a l'avantage de mettre en cache les résultats des requêtes souvent appelées, contrairement à PHP. :)
Chapitre précédent Sommaire Chapitre suivant

Partager

16 commentaires pour "Les conditions"
Note moyenne : 3.87 / 4 (31 votes)
Pseudo Commentaire
Hors ligne Snorky # Posté le 30/05/2007 à 17:47:25
Trop fort le sdz!
Avatar

Ville : Herblay
Pays : France métropolitaine

Impressionnant, je ne connaissais pas toutes ces prossibilités o_O


Merci pour ton tuto!!!
Hors ligne MorbaC++ # Posté le 30/05/2007 à 19:25:46
Lol !
Avatar

Très bon tuto, merci à toi ;)

Par contre, allez savoir pourquoi, mes requêtes s'arrêtent à partir d'un certain nombre de caractères dans l'invité de commande de Windows XP, je ne pouvais donc pas écrire la reqûete en entière. Y a-t'il une façon de contourner le problème en divisant la requête en plusieurs lignes par exemple ?

Merci.
 
Hors ligne OAO # Posté le 31/05/2007 à 21:18:11
Avatar

Beau travail :)

Une petite erreur dans une requete chapitre "traitement des nulls": il devrait y avoir Code : SQL
SELECT id, IFNULL(points, 0) / COALESCE(points, 1) AS a_deja_joue FROM ex_null;
au lieu de Code : SQL
SELECT id, IFNULL(points, 0) / COALESCE(points, 0) AS a_deja_joue FROM ex_null;
(sinon division par 0 :/ la copie d'écran est juste )

Et pour le dernier chapitre ta requete ne marche pas si tous les champs sont à -1 sauf le dernier: après Code : SQL
INSERT INTO probleme1 (i1, i2, i3, i4, i5, i6) VALUES (-1,-1,-1,-1,-1,NULL);
la dernière ligne a une somme à NULL.

Je pense qu'il faudrait changer Code : SQL
WHEN (((((i1 + 1 OR i2 + 1) OR i3 + 1) OR i4 + 1) OR i5 + 1) OR i6 + 1) IS NULL THEN 'NULL'
par Code : SQL
WHEN COALESCE(i1,i1,i3,i4,i5,i6) IS NULL THEN NULL


Ce qui donnerait Code : SQL
SELECT
        id, @somme := CASE
            WHEN COALESCE(i1,i1,i3,i4,i5,i6) IS NULL THEN NULL
                ELSE COALESCE(i1, 0) + COALESCE(i2, 0) + COALESCE(i3, 0) + COALESCE(i4, 0) + COALESCE(i5, 0) + COALESCE(i6, 0) END AS somme,
        6 + @somme - COALESCE(i1, 1) - COALESCE(i2, 1) - COALESCE(i3, 1) - COALESCE(i4, 1) - COALESCE(i5, 1) - COALESCE(i6, 1) AS nb_cols,
        i1, i2, i3, i4, i5, i6
FROM probleme1;


D'ailleurs pourquoi mets-tu des guillements autour de tes NULL?

En tout cas j'attend la suite avec impatience :) il y a qui de prévu? Les fonctions?

EDIT: et au niveau de la clareté celà ne serait pas mieux de mettre Code : SQL
COALESCE(i1, 0) + COALESCE(i2, 0) + COALESCE(i3, 0) + COALESCE(i4, 0) + COALESCE(i5, 0) + COALESCE(i6, 0) AS nb_cols
Celà évite 2 opérations (+6 et +@somme) et évite de rechercher une variable donc ca ne devrait pas être plus lent au contraire.. non?
 
Hors ligne Doomsayer # Posté le 08/04/2009 à 23:20:16
Avatar

Superbe tuto, pas facile le TP... je n'aurais jamais pensé à faire comme ça pour le nombre de colonnes non nulles. :p

Par contre pour la condition "si toutes les colonnes ont le marqueur NULL", il me semble que c'est quand même plus simple et plus rapide d'utiliser :
Code : SQL
1
... CASE WHEN ((i1 IS NULL) AND (i2 IS NULL) AND (i3 IS NULL) AND (i4 IS NULL) AND (i5 IS NULL) AND (i6 IS NULL)) THEN 'NULL' ...


ça évite également la faille mentionnée par Zimm i48 !
Hors ligne christophetd # Posté le 29/06/2009 à 14:38:14
Regardez-moi !
Avatar

Avis : Très bon
Flux RSS

Ville : Gap
Pays : France métropolitaine

Encore un super chapitre, et un TP assez difficile. :p
Par contre, j'ai obtenu une requête (qui marche) complètement différente de la tienne :
Secret (cliquez pour afficher)

Code : SQL
1
2
3
4
5
SELECT id, 
IF(i1 IS NULL, 0, i1)+IF(i2 IS NULL, 0, i2)+IF(i3 IS NULL, 0, i3)+IF(i4 IS NULL, 0, i4)+IF(i5 IS NULL, 0, i5)+IF(i6 IS NULL, 0, i6) AS somme,
IF(i1 IS NULL, 0, 1)+IF(i2 IS NULL, 0, 1)+IF(i3 IS NULL, 0, 1)+IF(i4 IS NULL, 0, 1)+IF(i5 IS NULL, 0, 1)+IF(i6 IS NULL, 0, 1) AS nb_colonnes , 
i1, i2, i3, i4, i5
FROM probleme1;



Je sais que tu avais dit que l'on pouvait trouver le nombre de colonnes sans conditions, mais je ne voyais pas comment faire. :-°

Encore bravo.
 

Voir tous les commentaires