Table of Contents
Sous requête
Une sous-requête (subquery) est une requête SQL placée à l’intérieur d’une autre requête. Elle est généralement entourée de parenthèses avec un nom d'alias et peut apparaître dans
- la clause WHERE
- la clause FROM (on parle alors de table dérivée)
- la clause SELECT
- parfois dans HAVING
Il existe deux types de sous requêtes :
- La sous-requête corrélée qui dépend de la requête principale. Elle est donc réévaluée pour chaque ligne.
- La sous-requête non corréléé qui est indépendante et exécuté une seule fois.
Dans la clause FROM, une sous-requête est toujours non corrélée. Parce qu’elle doit produire une table (un jeu de résultats) indépendante avant d’être "reliée" au reste de la requête principale.
- Dans le SELECT, une sous-requête peut être corrélée (elle utilise des colonnes de la requête principale) ou non.
- Dans le WHERE, idem : elle peut être corrélée (EXISTS, IN dépendant de la ligne en cours) ou non (test fixe).
Sous-requête scalaire
une sous-requête scalaire est une sous-requête qui retourne une seule valeur (un scalaire, c’est-à-dire une seule ligne et une seule colonne).
Elle peut être utilisée partout où une valeur simple est attendue, par exemple :
- dans la liste de sélection (SELECT),
- dans une clause WHERE,
- dans une clause HAVING,
- parfois même dans la clause ORDER BY.
SELECT *
FROM table
WHERE nom_colonne = (
SELECT valeur
FROM table2
LIMIT 1
)
Exercice : Sélectionner les articles dont le prix est supérieur à la moyenne générale des prix des articles.
Dans un langage procédural on utiliserait deux étapes pour obtenir le résultat. Première étape calculer la moyenne des prix
select avg(prix) from article
>> 5.8060418562329390
Puis dans un deuxième temps faire la requête de sélection avec la valeur trouvée.
select * from article where prix > 5.8060418562329390
Il est possible d'utiliser une sous requête à l'intérieur de la requête principale. La sous requête calcule la moyenne à l'endroit nécessaire.
select * from article
where prix > (select avg(prix) from article);
Exercice : Sélectionner les articles dont le prix est supérieur à la moyenne des prix des articles de la même famille.
Cette fois ci il faut calculer les moyennes des prix pour chacune des familles et comparer les articles avec la moyenne correspondante. Ce n'est plus possible en programmation procédurale.
select avg(prix) from article where famille_code = '02CHOU';
select * from article where prix > 3.68 and famille_code = '02CHOU';
select avg(prix) from article where famille_code = '02NARU';
select * from article where prix > 2.47 and famille_code = '02NARU';
La sous requête permet de calculer pour chaque ligne d'article la moyenne correspondante à la famille du dit article.
select code, article, prix from article a1
where prix > (
select avg(prix) from article
where famille_code = a1.famille_code
)
Remarquez l'utilisation de l'alias a1 sur la table de la requête principale pour pouvoir être injecter sans confusion dans la sous requête car celle-ci utilise la même table.
Conditions
Exists
Exists teste l’existence d’au moins une ligne dans la sous-requête.
Renvoie TRUE si la sous-requête retourne une ou plusieurs lignes.
SELECT nom
FROM clients c
WHERE EXISTS (
SELECT 1
FROM commandes o
WHERE o.client_id = c.id
);
Le contenu du SELECT dans EXISTS n’a pas d’importance (on met parfois SELECT 1 par convention).
NOT EXISTS inverse la condition.
IN / NOT IN
IN teste si une valeur est contenue dans l’ensemble retourné par la sous-requête.
SELECT nom
FROM produits
WHERE categorie_id IN (
SELECT id FROM categories WHERE actif = true
);
NOT IN inverse la condition.
SOME
ANY permet de comparer une valeur avec le résultat d’une sous-requête. Il est ainsi possible de vérifier si une valeur est égale, différente, supérieure, inférieur pour au moins une des valeurs de la sous-requête.
SELECT nom
FROM produits
WHERE prix > SOME (
SELECT prix_promo FROM promotions
);
ALL
ALL permet de comparer une valeur avec le résultat d’une sous-requête. Il est ainsi possible de vérifier si une valeur est égale, différente, supérieure, inférieur pour toutes valeurs de la sous-requête.
SELECT nom
FROM produits
WHERE prix > ALL (
SELECT prix_promo FROM promotions
);
Lateral
Une sous-requête LATERAL (ou sous-requête latérale) permet à une sous-requête dans la clause FROM d’accéder aux colonnes déjà définies dans les relations qui la précèdent.
Exercice : Afficher pour chaque adhérent la date et le montant de son dernier ticket.
select a.nom, a.prenom, dernier.date_ticket,
dernier.montant
from adherent a
join lateral (
select *
from ticket_completZ
where adherent_id = a.id
order by date_ticket desc -- tri descendant
limit 1
) dernier on dernier.adherent_id = a.id;
CTE
Une requête CTE (Common Table Expression) est une construction SQL qui permet de définir une sous-requête temporaire, nommée, que l’on peut ensuite réutiliser dans une requête principale.
Une CTE est comme une vue temporaire locale à la requête. Sa définition n’est pas stockée dans la base (contrairement à une vue). Elle améliore la clarté et la modularité des requêtes SQL.
Elle est particulièrement utile pour :
- améliorer la lisibilité des requêtes complexes,
- factoriser du code (éviter de répéter la même sous-requête),
- faciliter la gestion de requêtes récursives (ex. parcours d’un arbre ou d’une hiérarchie).
WITH total_par_client AS (
SELECT client_id, SUM(montant) AS total
FROM ventes
GROUP BY client_id
)
SELECT c.client_id, c.total
FROM total_par_client c
WHERE c.total > 1000;
Récursivité
Une requête CTE peut se réutiliser elle-même grâce au mot-clé RECURSIVE, ce qui permet de parcourir un arbre (organisation d’entreprise, structure de fichiers, etc.).
WITH RECURSIVE hierarchie AS (
SELECT id, nom, manager_id, 1 AS niveau
FROM employes
WHERE manager_id IS NULL
UNION ALL -- Ajout
SELECT e.id, e.nom, e.manager_id, h.niveau + 1
FROM employes e
JOIN hierarchie h ON e.manager_id = h.id
)
SELECT * FROM hierarchie;