⌂ Bases de données
Fiche 9 sur 12

Interroger : le SELECT

Aller chercher exactement les données dont on a besoin.

Après cette fiche, vous saurez :

1La forme de base

SELECT titre, annee FROM livres;
Résultat
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 :

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;
Résultat
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;
Résultat
titre                     | annee
--------------------------+------
Le Chien jaune            | 1931
Stupeur et tremblements   | 1999
Le Petit Prince           | 1943
OpérateurSignifieExemple
=égal àWHERE annee = 1885
<>différent deWHERE nationalite <> 'Belge'
> <plus grand, plus petitWHERE annee > 1900
>= <=ou égalWHERE annee <= 1900
BETWEENentre deux valeurs, bornes comprisesWHERE annee BETWEEN 1900 AND 1950
INdans une listeWHERE num_auteur IN (1, 3)
LIKEressemble à (sensible à la casse)WHERE titre LIKE 'Le %'
ILIKEressemble à (insensible à la casse)WHERE titre ILIKE 'le %'
IS NULLest videWHERE date_retour IS NULL

LIKE et les jokers

MotifTrouve
'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 jaune

Le 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érateurIl faut que…
ANDles deux conditions soient vraies
ORau moins une des deux soit vraie
NOTla 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;   -- correct

Pourquoi ? 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;
Résultat
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-05

5Trier et limiter

SELECT titre, annee
FROM livres
ORDER BY annee;
ÉcritureEffet
ORDER BY anneeDu plus petit au plus grand (par défaut)
ORDER BY annee ASCPareil, mais explicite
ORDER BY annee DESCDu plus grand au plus petit
ORDER BY nom, prenomPar nom, puis par prénom à égalité
-- Les trois livres les plus récents
SELECT titre, annee
FROM livres
ORDER BY annee DESC
LIMIT 3;
Résultat
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;
Résultat
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.

1Vos premières requêtesRecopier

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.

2Écrire vos requêtesModifier

Écrivez la requête qui répond à chaque question.

  1. Les livres parus entre 1900 et 1950, triés du plus ancien au plus récent.
  2. Les auteurs qui ne sont pas belges.
  3. Les lecteurs dont le nom commence par un D ou un M.
  4. Les emprunts encore en cours, avec le nombre de jours écoulés depuis l'emprunt.
  5. 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.

3Interroger votre baseCréer

Sur votre propre base.

  1. Écrivez cinq requêtes utiles au métier de votre organisation.
  2. Au moins une avec ORDER BY, une avec IS NULL, une avec un calcul, une avec ILIKE.
  3. 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

Une case qui coince ? Relisez la partie concernée avant de passer à la fiche suivante.

← Fiche 8 Fiche 10 : Les jointures →