Interroger : le SELECT
Aller chercher exactement les données dont on a besoin.
Après cette fiche, vous saurez :
- choisir les colonnes et filtrer les lignes
- utiliser les opérateurs de comparaison
- trier et limiter les résultats
- traiter correctement les valeurs vides
1La forme de base
SELECT titre, annee FROM livres;
titre | annee --------------------------+------ Germinal | 1885 L'Assommoir | 1877 Le Chien jaune | 1931 Stupeur et tremblements | 1999 Le Petit Prince | 1943
Deux mots suffisent : SELECT dit quelles colonnes,
FROM dit quelle table.
L'astérisque demande toutes les colonnes :
SELECT * FROM livres;
Attention
Évitez SELECT * dans un vrai travail.
C'est pratique pour explorer, mais dans une requête qu'on garde, on nomme les colonnes. Trois raisons :
- on ne transporte que ce dont on a besoin ;
- on sait ce qu'on reçoit, même si la table change ensuite ;
- on comprend la requête en la relisant.
Renommer une colonne : AS
SELECT titre AS "Titre du livre", annee AS "Année de parution" FROM livres;
AS change le nom affiché. Utile pour un état de sortie destiné à
quelqu'un qui ne connaît pas la base. Les guillemets doubles permettent les espaces
et les accents.
Éviter les doublons : DISTINCT
SELECT DISTINCT nationalite FROM auteurs;
nationalite ------------ Française Belge
Quatre auteurs, mais seulement deux nationalités différentes.
2WHERE : filtrer les lignes
SELECT titre, annee
FROM livres
WHERE annee > 1900;titre | annee --------------------------+------ Le Chien jaune | 1931 Stupeur et tremblements | 1999 Le Petit Prince | 1943
| Opérateur | Signifie | Exemple |
|---|---|---|
= | égal à | WHERE annee = 1885 |
<> | différent de | WHERE nationalite <> 'Belge' |
> < | plus grand, plus petit | WHERE annee > 1900 |
>= <= | ou égal | WHERE annee <= 1900 |
BETWEEN | entre deux valeurs, bornes comprises | WHERE annee BETWEEN 1900 AND 1950 |
IN | dans une liste | WHERE num_auteur IN (1, 3) |
LIKE | ressemble à (sensible à la casse) | WHERE titre LIKE 'Le %' |
ILIKE | ressemble à (insensible à la casse) | WHERE titre ILIKE 'le %' |
IS NULL | est vide | WHERE date_retour IS NULL |
LIKE et les jokers
| Motif | Trouve |
|---|---|
'Le %' | tout ce qui commence par « Le » |
'%prince' | tout ce qui finit par « prince » |
'%chien%' | tout ce qui contient « chien » |
'_ermina_' | un caractère quelconque : Germinal, Terminal… |
À retenir
ILIKE est propre à PostgreSQL, et il est très pratique.
SELECT titre FROM livres WHERE titre LIKE '%chien%'; -- 0 résultat
SELECT titre FROM livres WHERE titre ILIKE '%chien%'; -- Le Chien jauneLe titre contient « Chien » avec une majuscule. LIKE ne le trouve
pas, ILIKE si.
Pour une recherche saisie par un utilisateur, prenez toujours
ILIKE : personne ne tape les majuscules correctement.
3Combiner les conditions
SELECT titre, annee FROM livres WHERE annee > 1900 AND num_auteur = 2;
| Opérateur | Il faut que… |
|---|---|
AND | les deux conditions soient vraies |
OR | au moins une des deux soit vraie |
NOT | la condition soit fausse |
Attention
AND est prioritaire sur OR. C'est un piège classique.
SELECT titre FROM livres WHERE annee = 1885 OR annee = 1877 AND num_auteur = 2;
PostgreSQL comprend :
WHERE annee = 1885 OR (annee = 1877 AND num_auteur = 2)
Ce n'est probablement pas ce que vous vouliez. Mettez toujours des parenthèses dès que vous mélangez AND et OR :
WHERE (annee = 1885 OR annee = 1877) AND num_auteur = 2
4Les valeurs vides
Rappel de la fiche 6 : NULL veut dire « on ne sait pas ». Ce n'est ni
zéro, ni une chaîne vide.
Attention
On ne compare jamais NULL avec le signe égal.
SELECT * FROM emprunts WHERE date_retour = NULL; -- 0 ligne, toujours
SELECT * FROM emprunts WHERE date_retour IS NULL; -- correctPourquoi ? Parce que NULL = NULL ne vaut pas « vrai » : ça vaut
« on ne sait pas ». Deux informations inconnues ne sont pas forcément égales.
Retenez la formule : IS NULL et IS NOT NULL.
Jamais = NULL.
-- Les emprunts en cours (pas encore rendus) SELECT num_emprunt, date_emprunt, date_prevue FROM emprunts WHERE date_retour IS NULL;
num_emprunt | date_emprunt | date_prevue
------------+--------------+------------
2 | 2026-09-05 | 2026-09-26
3 | 2026-09-10 | 2026-10-01
4 | 2026-08-15 | 2026-09-055Trier et limiter
SELECT titre, annee FROM livres ORDER BY annee;
| Écriture | Effet |
|---|---|
ORDER BY annee | Du plus petit au plus grand (par défaut) |
ORDER BY annee ASC | Pareil, mais explicite |
ORDER BY annee DESC | Du plus grand au plus petit |
ORDER BY nom, prenom | Par nom, puis par prénom à égalité |
-- Les trois livres les plus récents
SELECT titre, annee
FROM livres
ORDER BY annee DESC
LIMIT 3;titre | annee --------------------------+------ Stupeur et tremblements | 1999 Le Petit Prince | 1943 Le Chien jaune | 1931
À retenir
L'ordre des mots-clés est imposé.
SELECT colonnes FROM table WHERE conditions ORDER BY tri LIMIT nombre;
On ne peut pas mettre ORDER BY avant WHERE. Apprenez cet
ordre par cœur : c'est toujours le même, et il ne changera plus, même quand nous
ajouterons GROUP BY à la fiche 11.
6Calculer dans un SELECT
Une colonne du résultat peut être un calcul.
SELECT titre,
annee,
2026 - annee AS age_du_livre
FROM livres
ORDER BY age_du_livre DESC;titre | annee | age_du_livre --------------------------+-------+------------- L'Assommoir | 1877 | 149 Germinal | 1885 | 141 Le Chien jaune | 1931 | 95 Le Petit Prince | 1943 | 83 Stupeur et tremblements | 1999 | 27
La colonne age_du_livre n'existe pas dans la table. Elle est calculée
au moment de la requête. C'est exactement ce dont on a besoin pour un état de
sortie.
-- Les emprunts en retard
SELECT num_emprunt,
date_prevue,
CURRENT_DATE - date_prevue AS jours_de_retard
FROM emprunts
WHERE date_retour IS NULL
AND date_prevue < CURRENT_DATE;Soustraire deux dates en PostgreSQL donne un nombre de jours. Très pratique.
→Exercices
Toutes ces requêtes portent sur la base de la bibliothèque des fiches 7 et 8.
Exécutez ces cinq requêtes et notez le nombre de lignes obtenues.
-- 1 SELECT nom, prenom FROM auteurs; -- 2 SELECT titre FROM livres WHERE annee < 1900; -- 3 SELECT * FROM lecteurs ORDER BY nom; -- 4 SELECT titre FROM livres WHERE titre ILIKE '%le%'; -- 5 SELECT DISTINCT nationalite FROM auteurs;
Solution
1. 4 lignes — tous les auteurs.
2. 2 lignes — Germinal (1885) et L'Assommoir (1877).
3. 3 lignes, triées : Benali, Dupuis, Martin.
4. 4 lignes. Et c'est instructif : ILIKE '%le%' trouve
Le Chien jaune, Le Petit Prince, L'Assommoir (« l'Assommoir »
contient bien « le »… non, en fait il contient « L'A »).
Vérifiez vous-même : le motif %le% cherche les deux lettres l et e
côte à côte, n'importe où. On les trouve dans « Le Chien », « Le Petit », et dans
« Stupeur et tremblements ». Soit 3 lignes.
La leçon : LIKE et ILIKE ramènent souvent plus que
prévu. Vérifiez toujours le résultat plutôt que de supposer.
5. 2 lignes — Française et Belge.
Écrivez la requête qui répond à chaque question.
- Les livres parus entre 1900 et 1950, triés du plus ancien au plus récent.
- Les auteurs qui ne sont pas belges.
- Les lecteurs dont le nom commence par un D ou un M.
- Les emprunts encore en cours, avec le nombre de jours écoulés depuis l'emprunt.
- Le livre le plus ancien de la base.
Solution
-- 1 SELECT titre, annee FROM livres WHERE annee BETWEEN 1900 AND 1950 ORDER BY annee ASC; -- 2 SELECT nom, prenom, nationalite FROM auteurs WHERE nationalite <> 'Belge'; -- 3 SELECT nom, prenom FROM lecteurs WHERE nom ILIKE 'D%' OR nom ILIKE 'M%'; -- 4 SELECT num_emprunt, date_emprunt, CURRENT_DATE - date_emprunt AS jours_ecoules FROM emprunts WHERE date_retour IS NULL; -- 5 SELECT titre, annee FROM livres ORDER BY annee ASC LIMIT 1;
Sur la 2 : attention, si un auteur avait nationalite à
NULL, il ne sortirait pas dans le résultat. Une comparaison avec
NULL ne donne jamais « vrai ». Pour l'inclure, il faudrait écrire :
WHERE nationalite <> 'Belge' OR nationalite IS NULL
C'est un piège qui coûte cher en situation réelle.
Sur la 3 : on pourrait aussi écrire
WHERE LEFT(nom, 1) IN ('D', 'M'). Les deux fonctionnent ; la version avec
ILIKE est plus lisible pour un débutant.
Sur la 5 : ORDER BY + LIMIT 1 est la façon simple
de trouver un extrême. Nous verrons à la fiche 11 la fonction MIN(), qui
répond autrement à la même question.
Sur votre propre base.
- Écrivez cinq requêtes utiles au métier de votre organisation.
- Au moins une avec
ORDER BY, une avecIS NULL, une avec un calcul, une avecILIKE. - Pour chacune, écrivez en commentaire la question en français à laquelle elle répond.
Solution
-- Quels patients n'ont pas de numéro de téléphone ?
SELECT nom, prenom, date_naissance
FROM patients
WHERE telephone IS NULL;
-- Quel âge ont mes patients, du plus âgé au plus jeune ?
SELECT nom,
prenom,
date_naissance,
EXTRACT(YEAR FROM AGE(date_naissance)) AS age
FROM patients
ORDER BY date_naissance ASC;
-- Quelles consultations ont eu lieu cette semaine ?
SELECT num_consultation, date_consult, heure, montant
FROM consultations
WHERE date_consult >= CURRENT_DATE - 7
ORDER BY date_consult DESC, heure DESC;
-- Quelles consultations ont été enregistrées sans motif ?
SELECT num_consultation, date_consult, montant
FROM consultations
WHERE motif IS NULL;
-- Quels médecins ont une spécialité contenant "derm" ?
SELECT nom, prenom, specialite
FROM medecins
WHERE specialite ILIKE '%derm%';Deux fonctions utiles introduites ici :
AGE(date)calcule la durée écoulée depuis une date.EXTRACT(YEAR FROM …)en tire le nombre d'années.
C'est la bonne façon de gérer un âge : on stocke la date de naissance, et on calcule l'âge au moment où on en a besoin. Il est toujours juste.
Ce qui est évalué dans cet exercice n'est pas la virtuosité SQL. C'est que vos requêtes répondent à de vraies questions métier. Une requête qui ne sert à personne, même parfaitement écrite, ne vaut rien.
Je sais faire
- Je sais choisir mes colonnes plutôt que d'écrire *.
- Je sais filtrer avec WHERE et combiner avec AND et OR.
- Je sais trier avec ORDER BY.
- Je sais tester une valeur vide avec IS NULL.
Une case qui coince ? Relisez la partie concernée avant de passer à la fiche suivante.