9
sousrequete
medina5 edited this page 2025-09-10 21:59:34 +02:00
This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Sous requête

Une sous-requête (subquery) est une requête SQL placée à lintérieur dune 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 quelle 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, cest-à-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 lexistence dau 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 na pas dimportance (on met parfois SELECT 1 par convention).

NOT EXISTS inverse la condition.

IN / NOT IN

IN teste si une valeur est contenue dans lensemble 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 dune 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 dune 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 daccé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 lon peut ensuite réutiliser dans une requête principale.

Une CTE est comme une vue temporaire locale à la requête. Sa définition nest 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 dun arbre ou dune 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 dentreprise, 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;