Les sous-requêtes

Avec SQL il est possible d'imbriquer des requêtes. On parlera de ‘sous-requêtes' ou ‘requêtes imbriquées' ou encore ‘requêtes en cascades'.

Une sous-requête peut-être utilisée pour renvoyer un résultat dans chacune des clauses de la requête principale SELECT, WHERE, FROM.

Ceci permet de résoudre élégamment des problèmes complexes ou d'optimiser les performances des requêtes.

A noter qu'il est en général possible de décomposer un problème complexe en problèmes intermédiaires plus simple en créant par exemple des tables temporaires, mais les performances risquent d'être dégradées.

MéthodeSous-requêtes renvoyant une valeur unique

Une requête renvoyant une valeur unique peut-être imbriquée partout ou une constante peut figurer dans la requête principale.

Exemple dans la clause SELECT:

SELECT "Nom_Commune", round(("Population" / (SELECT avg(a."Population") FROM travail.communes64 AS a)) ::numeric ,2) AS "Population/Moyenne" FROM travail.communes64

renvoie le rapport de la population à la moyenne du département pour chaque commune :

Exemple dans la clause WHERE (le plus usité) :

On recherche les communes dont la population est supérieure à la moyenne des populations des communes.

SELECT "Nom_Commune", "Population" FROM travail.communes64 WHERE "Population" > (SELECT avg("Population") FROM travail.communes64) ORDER BY "Population"

renvoi (extrait):

MéthodeL'instruction WITH (Common Table expressions)

WITH fournit une façon d'écrire les sous-requêtes pour utilisation dans une requête SELECT plus étendue. Les sous-requêtes, qui sont souvent appelées des expressions communes de tables (Common Table Expressions) ou CTE, peuvent être considérées comme la déclaration d'une table temporaire n'existant que pour la requête. Une utilisation de cette fonctionnalité est de découper des requêtes complexes en parties plus simples.

Exemple :

Sans WITH

SELECT * FROM (SELECT * FROM MatableA) AS Monsubset

Avec WITH :

WITH Monsubset AS (SELECT * FROM MatableA)

SELECT * FROM Monsubset

Chaque ordre auxiliaire dans une clause WITH peut être un SELECT, INSERT, UPDATE, ou DELETE; et la clause WITH elle même est attachée à un ordre primaire qui peut lui aussi être un SELECT, INSERT, UPDATE, ou DELETE.

On trouvera des exemples plus pertinents dans la documentation de PostgreSQL

MéthodeSous-requête renvoyant une liste

Une sous-requête peut renvoyer une liste de valeurs (c'est-à-dire une colonne).

Dans ce cas elle ne peut être utilisée que comme critère de comparaison avec certains opérateurs comme IN (ou ALL, ou ANY).

Exemple : On recherche les N° INSEE des communes qui sont dans des structures de plus de 20 membres.

select code_insee FROM travail."Delegation" WHERE code_siren IN (SELECT n_siren FROM travail."Structure" WHERE nb_membres > 20)

beaucoup de requêtes utilisant le IN (comme le NOT IN) peuvent être simplifiées en utilisant des jointures. En général les performances seront meilleures en utilisant une jointure que dans le cas d'une sous-requête avec [NOT] IN.

Dans notre exemple :

SELECT

"Delegation".code_insee

FROM

travail."Delegation",

travail."Structure"

WHERE

"Structure".n_siren = "Delegation".code_siren AND

"Structure".nb_membres > 20;

Pour en savoir plus on pourra par exemple consulter le SQL de A à Z sur les sous-requêtes.

MéthodeSous-requête vide ou non vide

Une requête renvoyant des valeurs ou pas peut-être imbriquée dans un prédicat EXISTS, UNIQUE ou MATCH.

Par exemple, Le prédicat EXISTS permet de tester l'existence ou l'absence de données dans la sous-requête. Si la sous-requête renvoie au moins une ligne, même remplie de marqueurs NULL, le prédicat est vrai. Dans le cas contraire le prédicat à pour valeur faux.

Exemple :

SELECT sum(a."Population")FROM travail.communes64 AS a WHERE EXISTS(SELECT * FROM travail.communes64 AS b WHERE b."Population" > 1000)

renvoi

SELECT sum(a."Population")FROM travail.communes64 AS a WHERE EXISTS(SELECT * FROM travail.communes64 AS b WHERE b."Population" > 10)

renvoi

car dans ce cas le select sous EXISTS renvoie au moins une valeur.

On obtient le même résultat que

SELECT sum(a."Population")FROM travail.communes64 AS a

Notre exemple est donc peu pertinent, mais montre le principe de l'utilisation du EXISTS.