Langage Manipulation de Données 2 : requêtes imbriquées
2025-09-26
Le résultat d’une requête SQL est un ensemble de tuples… donc une relation.
Les opérateurs ensemblistes (\(\cap\), \(\cup\), \(\setminus\)) permettent de combiner les résultats de deux requêtes (ou plus).
Important
Les requêtes combinées doivent avoir des schémas compatibles :
même nombre de colonnes,
même type de données pour les colonnes de même position.
INTERSECT, UNION, EXCEPTINTERSECT : tuples de l’intersection des résultats des requêtes.
UNION : tuples de l’union des résultats des requêtes.
EXCEPT : tuples de la différence ensembliste des résultats des requêtes
Note
UNION ne conserve pas les tuples doublons entre les deux requêtes.
Pour les conserver, il faut utiliser l’opérateur UNION ALL.
EXCEPTConsidérons les tables de schémas suivants :
Fournisseurs qui ont livré la pièce x22 mais pas la pièce x21
WHEREWHEREDans la clause WHERE d’une requête, on peut utiliser une autre requête, appelée sous-requête ou requête imbriquée.
La sous-requête est exécutée pour chaque tuple formé par la clause FROM. Ce tuple sera sélectionné en fonction du test effectué sur le résultat de la sous-requête.
Dans une sous-requête, on peut se référer aux tables nommées dans la clause FROM de la requête de niveau supérieur.
IN, EXISTS, ALL, ANY sont des fonctions (ou opérations) qui prennent en argument une sous-requête.
Elles effectuent des tests sur l’ensemble des résultats d’une sous-requête.
Mise en garde
Attention au comportement de chaque opérateur si le résultat de la sous-requête est vide ou s’il contient la valeur NULL : il faut appliquer la logique trivalente à l’évaluation de l’opérateur.
INL’opérateur IN permet de tester si une valeur est présente dans le résultat d’une sous-requête.
Evaluée à vraie si <attribut> appartient au résultat de la sous-requête.
Mise en garde
Si le résultat de la sous-requête est vide, IN renvoie faux, NOT IN renvoie vrai.
Si <attribut> vaut NULL, IN et NOT IN renvoient UNKNOWN.
L’évaluation de IN s’effectue par un OU entre des tests \(=\). Pour les valeurs NULL du résultat de la sous-requête, le test d’égalité retourne UNKNOWN. Donc si <attribut> est présent dans le résultat, IN retourne True sinon UNKNOWN.
L’évaluation de NOTIN s’effectue par un ET entre des tests \(\neq\). Donc si le résultat de la sous-requête contient au moins une valeur NULL, NOT IN retourne UNKNOWN.
INLister les villes de fournisseurs qui ont livré la pièce ‘x21’.
INNote
Pratique à utiliser sous la forme NOT IN pour coder l’opération de différence.
Lister les noms de pièces pour lesquelles il n’y a jamais eu de livraison.
Equivalence en algèbre relationnelle :
\[\pi_{\text{NomP}}(\text{Piece}) - \pi_{\text{NomP}}(\text{Livraison})\]
INLes capitales dont la population est plus grande que la moitié de la population du pays.
ANY, ALLCe sont des quantificateurs qui s’utilisent dans des tests.
ANY : évalué à vrai si au moins un résultat de la sous-requête vérifie le test.ALL : évalué à vraie si tous les résultats de la sous-requête vérifient le test.Note
<attributs>: peut être une liste d’attributs
ANYAlternative au IN.
Lister les noms de fournisseurs qui ont livré la pièce ‘x21’.
Mise en garde
Si le résultat de la sous-requête est vide, ANY renvoie False.
Si l’attribut NomF vaut NULL, le résultat de la comparaison avec ANY est UNKNOWN.
L’évaluation de ANY s’effectue par un OU entre des tests =|<>|<=|<|>|=>. Pour les valeurs NULL du résultat de la sous-requête, le test retourne UNKNOWN. Donc si le test est vrai pour un tuple du résultat, ANY retourne True sinon UNKNOWN.
ALLLa ville la plus peuplée de chaque pays.
Mise en garde
Si le résultat de la sous-requête est vide, ALL renvoie True.
Si l’attribut population vaut NULL, le résultat de la comparaison avec ALL est UNKNOWN.
L’évaluation de ALL s’effectue par un ET entre des tests =|<>|<=|<|>|=>. Pour les valeurs NULL du résultat de la sous-requête, le test retourne UNKNOWN. Donc si le résultat de la sous-requête contient au moins une valeur NULL, ALL retourne UNKNOWN.
ALLLes pays dont toutes les villes ont plus de 1 000 000 habitants.
La requête :
renvoie également les pays qui n’ont pas de ville !
On peut corriger en ajoutant un opérateur EXISTS sur la sous-requête.
Mais la meilleure solution est d’utiliser les fonctions d’agrégation que nous verrons plus tard.
C’est un quantificateur qui retourne un booléen.
EXISTS retourne vrai si le résultat de la sous-requête n’est pas vide.
Mise en garde
Si le résultat de la sous-requête ne contient que la valeur NULL, EXISTS renvoie vrai car le résultat de la sous-requête contient au moins un tuple.
EXISTSPays qui n’ont pas de ville
équivalent à
Note
NOT EXISTS permet souvent d’écrire une alternative à EXCEPT.
EXISTSNom des Fournisseurs ayant livré la pièce x21 mais qui n’ont jamais livré la pièce a22.
EXISTSLes régions qui ont au moins une langue officielle :
équivalent à
Renvoie un tuple unique avec une colonne unique (un scalaire).
Le plus souvent écrite avec une fonction d’agrégation (SUM, AVG, COUNT, MAX, MIN, …) que nous étudierons dans la suite du cours.
Utilisation possible dans une clause WHERE.
Exemple : lister les noms de pays dont la densité est inférieure à la densité mondiale.
Note
Ne pas confondre la densité mondiale moyenne (environ \(40\ \text{h/km}^2\) d’après la BDD world, en fait \(55\ \text{h/km}^2\) en 2025) avec la densité moyenne des pays (environ \(400\ \text{h/km}^2\)).
FROMDans une clause FROM, on peut écrire une sous-requête qui renvoie une table nommée avec un alias : on parle de table dérivée, dans le sens qu’elle dérive des tables existantes.
Note
Il s’agit d’une composition de requêtes.
FROMLister les pays (code, nom, espérance de vie) dont le PNB a progressé de 10% et dont la population de la capitale est supérieure à 2 millions.
SELECT bo.countrycode, name_country, lifeexpectancy
FROM
(
SELECT countrycode, name_country, lifeexpectancy, capital
FROM country
WHERE gnpold >0 AND gnp / gnpold > 1.1 -- test gnpold >0 pour gérer les valeurs nulles
) AS bo
JOIN
(
SELECT id, countrycode
FROM city
WHERE population > 2000000
) AS bc
ON bc.id=bo.capital ;LATERALNote
Dans l’exemple précédent, chaque table dérivée est calculée indépendemment puis la jointure est effectuée.
Avec l’opérateur LATERAL, on peut faire dépendre le calcul d’une table dérivée du résultat des tables précédentes du FROM.
LATERALLa table dérivée est calculée pour chaque tuple \(u\) obtenu par les opérations précédentes du FROM.
Puis chaque tuple \(v\) la table dérivée est concaténé à \(u\) par produit cartésien.
On peut aussi concaténer \(u\) et \(v\) avec une jointure.
LATERALPour chaque pays, donner la liste des villes de son térritoire.
Solution avec LATERAL
équivalent à
LATERALPlus intéressant : pour chaque pays, donner la ville la plus peuplée.
Evaluation : si la table dérivée est vide, le tuple \(u\) de la table country n’est pas retenu car \(\lbrace u\rbrace \times \emptyset = \emptyset\).
Donc les pays sans ville ne sont pas affichés.
WITHWITHUne clause WITH permet d’écrire des requêtes intermédiaires et d’alléger l’écriture d’une requête complexe.
Note
Ces expressions/requêtes sont appelées Common Table Expressions ou CTEs ou ETCs en français.
Les CTEs peuvent être des requêtes SELECT, mais aussi des expressions de manipulation (mise à jour, insertion, suppression) de données.
Les CTEs permettent de coller plus fidèlement à l’algèbre relationnelle que les requêtes imbriquées.
Note
Les CTEs permettent de composer des requêtes de même que les requêtes imbriquées dans les clauses WHERE et FROM.
Par rapport à ces deux autres techniques, les avantages sont :
amélioration de la lisibilité et de la maintenance des requêtes complexes.
une ETC peut être utilisée plusieurs fois dans une même requête.
WITHLister les pays (code, nom, espérance de vie) dont le PNB a progressé de 10% et dont la population de la capitale est supérieure à 2 millions.
WITH better_off AS (
SELECT countrycode, name_country, lifeexpectancy, capital
FROM country
WHERE gnpold >0 AND gnp / gnpold > 1.1 -- gnpold >0 pour gérer les valeurs nulles
),
big_capital AS (
SELECT id, countrycode
FROM city
WHERE population > 2000000
)
SELECT bo.countrycode, name_country, lifeexpectancy
FROM better_off bo JOIN big_capital bc ON
bc.id=bo.capital ;
Requêtes SQL imbriquées

MA15E045 – Bases de Données – L3 MIASHS – UParis Cité