Tutoriel sur les sous-requêtes SQL : types, sous-requêtes corrélées et EXISTE avec des exemples

  1. Accueil
  2. Blog
  3. Bases de données et SQL

Bases de données et SQL

Tutoriel sur les sous-requêtes SQL : Types, sous-requêtes corrélées et EXISTES avec exemples

Une sous-requête est une requête insérée dans une autre requête. Apprenez chaque type et où il correspond, comprenez correctement les sous-requêtes corrélées et évitez le piège NOT IN qui ne renvoie silencieusement aucune ligne.

9 min de lecture
Tutoriel sur les sous-requêtes SQL : types, sous-requêtes corrélées et EXISTS avec exemples

À retenir

  • Une sous-requête est une requête imbriquée dans une autre et la requête externe utilise son résultat.
  • Les sous-requêtes peuvent apparaître dans WHERE, FROM, SELECT et HAVING.
  • Une sous-requête corrélée fait référence à la ligne externe, elle est donc logiquement évaluée par ligne.
  • Préférez NOT EXISTS à NOT IN lorsque les valeurs NULL sont possibles.

Certaines questions nécessitent deux étapes pour répondre. « Quelles commandes sont supérieures à la moyenne ? » vous oblige à trouver d’abord la moyenne, puis à comparer chaque commande avec elle. Les sous-requêtes SQL vous permettent d’écrire les deux étapes dans une seule instruction.

Ce didacticiel couvre chaque type à l’aide de deux petits tableaux. Nous avons exécuté ces requêtes dans SQLite 3.50 et les résultats affichés proviennent de cette exécution. La syntaxe de sous-requête standard est largement prise en charge et nous soulignons les points où les bases de données diffèrent.

Qu’est-ce qu’une sous-requête ?

Une sous-requête, également appelée requête interne ou imbriquée, est une instruction SELECT placée à l’intérieur d’une autre instruction, entre parenthèses. Conceptuellement, la requête interne produit un résultat que la requête externe utilise ensuite.

SELECT id, total
FROM orders
WHERE total > (SELECT AVG(total) FROM orders);

Les exemples de tableaux

CREATE TABLE customers (id INT, name TEXT, city TEXT);
CREATE TABLE orders (id INT, customer_id INT, total INT);

INSERT INTO customers VALUES
 (1,'Asha','Pune'), (2,'Ben','Delhi'), (3,'Chitra','Pune'), (4,'Dev','Goa');

INSERT INTO orders VALUES
 (101,1,1200), (102,1,800), (103,2,300), (104,3,2500), (105,3,100);

Notez que Dev n’a aucune commande. Ce détail est important pour plusieurs exemples ci-dessous.

Types de sous-requêtes en un coup d’œil

Tapez Retours Emplacement typique Opérateurs courants
Scalaire Une valeur WHERE, SELECT =, >, <
Multi-rangées Une colonne, plusieurs lignes OÙ IN, ANY, ALL
Tableau (dérivé) Beaucoup de colonnes et de lignes DE Utilisé comme une table
Corrélé Cela dépend de la rangée extérieure WHERE, SELECT N’importe lequel, et EXISTS

Sous-requêtes scalaires

Une sous-requête scalaire renvoie exactement une valeur, vous pouvez donc l’utiliser partout où une valeur unique a du sens. Voici la requête précédente, avec son résultat. La moyenne de toutes les commandes est de 980 :

SELECT id, total
FROM orders
WHERE total > (SELECT AVG(total) FROM orders);

-- 101 | 1200
-- 104 | 2500

Vous ne pouvez pas écrire WHERE total > AVG(total), car les agrégats ne sont pas autorisés directement dans WHERE. La sous-requête contourne ce problème en calculant la valeur séparément.

Que se passe-t-il si une sous-requête « scalaire » renvoie plusieurs lignes ? La plupart des bases de données, notamment PostgreSQL, MySQL et SQL Server, génèrent une erreur. SQLite ne le fait pas. Dans notre test, il n’a utilisé silencieusement que la première ligne, ce qui pourrait cacher un vrai bug, alors ne vous fiez pas à ce comportement.

DANS et PAS DANS

Lorsque la requête interne renvoie une liste, utilisez IN. Cela permet de trouver les clients avec au moins une commande supérieure à 1 000 :

SELECT name FROM customers
WHERE id IN (SELECT customer_id FROM orders WHERE total > 1000);

-- Asha
-- Chitra

NOT IN fait le contraire, trouvant des clients qui n’ont jamais commandé :

SELECT name FROM customers
WHERE id NOT IN (SELECT customer_id FROM orders);

-- Dev

Cela fonctionne ici, mais NOT IN présente un cas limite dangereux, que nous abordons ci-dessous.

Sous-requêtes dans FROM

Une sous-requête dans FROM agit comme une table temporaire, souvent appelée table dérivée. C’est utile lorsque vous devez agréger deux fois, comme dans cette requête pour les dépenses moyennes par client dans chaque ville :

SELECT city, AVG(spent) AS avg_spent
FROM (
  SELECT c.city, SUM(o.total) AS spent
  FROM customers c
  JOIN orders o ON o.customer_id = c.id
  GROUP BY c.id, c.city
) AS per_customer
GROUP BY city;

-- Delhi |  300.0
-- Pune  | 2300.0

La requête interne totalise chaque client et la requête externe fait la moyenne de ces totaux. Donner un alias à la table dérivée est une bonne habitude, car certaines bases de données l’exigent. Si GROUP BY est nouveau pour vous, consultez SQL GROUP BY et HAVING.

Sous-requêtes dans SELECT

Une sous-requête scalaire peut également ajouter une colonne calculée. Celui-ci compte les commandes par client :

SELECT name,
       (SELECT COUNT(*) FROM orders o WHERE o.customer_id = c.id) AS order_count
FROM customers c;

-- Asha   | 2
-- Ben    | 1
-- Chitra | 2
-- Dev    | 0

Dev apparaît avec zéro. Une jointure interne aurait complètement supprimé Dev, car Dev n’a pas de lignes correspondantes. Vous pourriez obtenir le même résultat avec un LEFT JOIN et un GROUP BY, que nous expliquons dans Les jointures SQL expliquées.

Sous-requêtes corrélées

L’exemple précédent est également corrélé, puisque la requête interne fait référence à c.id de la requête externe. Une sous-requête non corrélée peut s’exécuter seule. Une valeur corrélée ne peut pas, car elle dépend d’une valeur de la ligne externe actuelle.

Cela permet de trouver les commandes supérieures à la moyenne de ce même client :

SELECT o.id, o.customer_id, o.total
FROM orders o
WHERE o.total > (
  SELECT AVG(i.total)
  FROM orders i
  WHERE i.customer_id = o.customer_id
);

-- 101 | 1 | 1200
-- 104 | 3 | 2500

Voici comment le lire. Pour la commande 101, la requête interne fait la moyenne des commandes du client 1 (1 200 et 800), ce qui donne 1 000. Puisque 1 200 est supérieur, la ligne est conservée. Pour l’ordre 102, la même moyenne s’applique, mais 800 n’est pas supérieur, elle est donc supprimée. La commande unique du client 2 est égale à sa propre moyenne, elle est donc également supprimée.

Caractéristique Non corrélé Corrélé
Utilise des valeurs de requête externes ? Non Oui
Peut-il fonctionner tout seul ? Oui Non
Exécution logique Une fois Une fois par rangée extérieure
Risque de performance Généralement faible Peut être placé en hauteur sur de grandes tables

“Une fois par ligne” décrit la logique, et les optimiseurs de requêtes les réécrivent souvent en quelque chose de beaucoup plus efficace. Néanmoins, une sous-requête corrélée lente mérite d’être étudiée. Un index sur la colonne intérieure, ici orders.customer_id, peut faire une différence majeure. Voir Explication des index SQL.

EXISTE et N’EXISTE PAS

EXISTS pose une question par oui ou par non : la requête interne renvoie-t-elle au moins une ligne ? Cela peut s’arrêter à la première correspondance, et ce que vous sélectionnez à l’intérieur n’a pas d’importance, c’est pourquoi les gens écrivent souvent SELECT 1.

SELECT c.name FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id);

-- Asha, Ben, Chitra

SELECT c.name FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id);

-- Dev

Le piège NOT IN NULL

C’est l’un des bogues de sous-requête les plus courants. Nous avons créé une table avec un ID réel et un NULL, puis avons exécuté les deux formulaires :

CREATE TABLE r (customer_id INT);
INSERT INTO r VALUES (1), (NULL);

SELECT name FROM customers
WHERE id NOT IN (SELECT customer_id FROM r);
-- (no rows at all)

SELECT c.name FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM r WHERE r.customer_id = c.id);
-- Ben, Chitra, Dev

Pourquoi le premier ne renvoie rien ? id NOT IN (1, NULL) signifie id <> 1 AND id <> NULL. Toute comparaison avec NULL est inconnue plutôt que vraie, donc la condition dans son ensemble ne peut jamais être vraie. Il n’y a aucun message d’erreur ni aucun avertissement, juste un résultat vide qui semble plausible.

Astuce : Utilisez NOT EXISTS pour les anti-jointures par défaut. Si vous utilisez NOT IN, ajoutez WHERE customer_id IS NOT NULL dans la sous-requête.

Sous-requête vs JOIN vs CTE

De nombreuses requêtes peuvent être écrites de plusieurs manières. Plus tôt, nous avons trouvé des clients avec une commande importante en utilisant IN. Voici le même résultat avec une jointure :

SELECT DISTINCT c.name FROM customers c
JOIN orders o ON o.customer_id = c.id AND o.total > 1000;

-- Asha
-- Chitra

Notez le DISTINCT. Une jointure peut produire des doublons si un client a plusieurs correspondances, alors que IN et EXISTS ne le peuvent pas.

Utiliser Quand
REJOINDRE Vous avez besoin de colonnes des deux tables
DANS ou EXISTE Il vous suffit de filtrer selon qu’une correspondance existe
Tableau dérivé Vous devez d’abord regrouper, et cela reste court
CTE La logique comporte plusieurs étapes ou est utilisée plusieurs fois
Fonction fenêtre Vous avez besoin d’un chiffre par groupe à côté de chaque ligne

Les performances varient selon la base de données et les données, et les optimiseurs modernes produisent souvent des plans similaires. Choisissez donc d’abord la forme la plus claire, puis vérifiez EXPLAIN si elle s’avère lente.

Erreurs courantes

  • Utilisation de = avec un résultat sur plusieurs lignes. Utilisez IN ou assurez-vous que la requête interne renvoie une ligne.
  • Utilisation de NOT IN sur une colonne nullable. Vous pouvez perdre silencieusement chaque ligne.
  • Utilisation d’alias peu clairs. Dans les requêtes corrélées, qualifiez chaque colonne, sinon elle pourrait être liée à la mauvaise table.
  • Imbrication profonde. Trois niveaux sont difficiles à lire, alors convertissez-les en CTE.
  • Ajout de ORDER BY à l’intérieur. Cela n’a généralement aucun effet sur le résultat externe et certaines bases de données le rejettent.

Pour conclure

Les sous-requêtes sont un moyen naturel d’exprimer des questions en deux étapes. Commencez par les sous-requêtes scalaires et IN, puis entraînez-vous à celles corrélées en raisonnant sur une seule ligne externe à la fois. Et rappelez-vous NOT EXISTS chaque fois que des valeurs NULL sont possibles. Pour vous entraîner, écrivez une question de trois manières, avec une sous-requête, une jointure et un CTE, et comparez les résultats et la lisibilité.

Questions fréquentes

Qu’est-ce qu’une sous-requête en SQL ?

Une sous-requête est une instruction SELECT imbriquée dans une autre instruction SQL, entre parenthèses. L’instruction externe utilise son résultat sous forme de valeur, de liste ou de tableau.

Qu’est-ce qu’une sous-requête corrélée ?

Il s’agit d’une sous-requête qui fait référence à une colonne de la requête externe. Cela dépend de la ligne externe actuelle, il ne peut donc pas fonctionner seul.

Qu’est-ce qui est le plus rapide, une sous-requête ou un JOIN ?

Cela dépend de la base de données et des données, et les optimiseurs produisent souvent des plans similaires. Écrivez d’abord la version la plus claire, puis comparez les plans d’exécution si les performances sont importantes.

Quelle est la différence entre IN et EXISTE ?

IN compare une valeur avec une liste de résultats. EXISTS vérifie si une ligne est renvoyée. Ils se comportent différemment avec les valeurs NULL lorsqu’ils sont annulés.

Une sous-requête peut-elle renvoyer plusieurs colonnes ?

Oui, lorsqu’il est utilisé dans FROM en tant que table dérivée, et certaines bases de données autorisent également les comparaisons de lignes. Une sous-requête scalaire ne doit renvoyer qu’une seule colonne et une seule ligne.

Continuez à apprendre

Travailler avec Stack Quora

Besoin d’aide pour les tests, WordPress ou le référencement ?

Nous aidons les équipes à proposer de meilleurs logiciels et à développer leurs sites. Dites-nous sur quoi vous travaillez et nous vous proposerons une prochaine étape claire.

Continuez à lire

Plus de guides sur tests, IA et développement moderne

Vous avez apprécié cet article ? Choisissez un sujet et accédez directement à nos nouveaux didacticiels.

Intelligence artificielle

Les hallucinations de l'IA expliquées : pourquoi les modèles de langage inventent les choses et comment les réduire

Les hallucinations de l'IA ont expliqué : ce qu'elles sont, pourquoi les modèles de langage les produisent, les types courants, comment les réduire …

Lire l'article →
DevOps

Variables d'environnement expliquées : fichiers .env, secrets et configuration correctement effectués

Variables d'environnement expliquées avec des exemples Python que nous avons exécutés : lecture et définition, pourquoi chaque valeur est une chaîne…

Lire l'article →
Développement logiciel

Limitation du débit de l'API expliquée : algorithmes, en-têtes et gestion des erreurs 429

Limitation du débit de l'API expliquée avec une simulation que nous avons exécutée : compartiment de jetons, fenêtre fixe, fenêtre glissante, 429 ré…

Lire l'article →
Automatisation des tests

Se moquer en Python : unittest.mock, patch et les erreurs qui font mentir les tests

Moquerie en Python avec unittest.mock, testée : Mock et return_value, side_effect, correctifs où un nom est utilisé, spec et autospec, dates moqueus…

Lire l'article →
Tests logiciels

Couverture du code expliquée : couverture des lignes par rapport aux branches et ce que signifie réellement 100 %

Couverture du code expliquée avec une véritable exécution de cover.py : couverture des lignes, des branches et des conditions, lecture d'un rapport,…

Lire l'article →
Bases de données et SQL

DELETE vs TRUNCATE vs DROP dans SQL : différences, restauration et quand les utiliser

DELETE vs TRUNCATE vs DROP expliqué avec SQL testé : ce que chacun supprime, restauration, déclencheurs, réinitialisation d'identité, vitesse et en …

Lire l'article →
Bases de données et SQL

Vues SQL expliquées : créez, utilisez et évitez les pièges, avec des exemples

Vues SQL expliquées avec les requêtes que nous avons exécutées : création d'une vue, résultats en direct, masquage des colonnes, performances, vues …

Lire l'article →
Développement logiciel

Interface TypeScript vs Type : différences réelles, testées avec le compilateur

Interface TypeScript vs type, vérifiée avec le vrai compilateur : ce que les deux peuvent faire, ce qu'un seul peut faire, la fusion des déclaration…

Lire l'article →
Développement logiciel

Python *args et **kwargs expliqués : fonctions flexibles avec des exemples clairs

Python *args et **kwargs expliqués avec le code que nous avons exécuté : emballage et déballage, ordre des paramètres, arguments de mot-clé uniqueme…

Lire l'article →
Développement logiciel

Fonctions Python Lambda : syntaxe, utilisations réelles et quand ne pas les utiliser

Fonctions Python lambda expliquées avec le code que nous avons exécuté : syntaxe, tri avec clé, carte et filtre, fermetures, piège de liaison tardiv…

Lire l'article →
Intelligence artificielle

Que sont les agents IA ? Comment ils fonctionnent, avec une simple boucle d'agent que vous pouvez exécuter

Que sont les agents IA ? Un guide simple sur la boucle d'agent, les outils, la mémoire et les garde-fous, avec une petite boucle Python que nous avo…

Lire l'article →
DevOps

Surveillance vs observabilité : la vraie différence, avec les journaux, les métriques et les traces

Surveillance et observabilité expliquées : échecs connus et inconnus, journaux, métriques et traces, présentation pas à pas des incidents de paiemen…

Lire l'article →
DevOps

Volumes Docker vs montages Bind : conserver les données du conteneur de la bonne manière

Volumes Docker et montages de liaison expliqués : pourquoi les données du conteneur disparaissent, volumes nommés, montages de liaison, tmpfs, exemp…

Lire l'article →
Automatisation des tests

TDD vs BDD : principales différences, une fonctionnalité conçue dans les deux sens et quand les utiliser

TDD vs BDD expliqué avec une fonctionnalité de remise construite dans les deux sens : refactor rouge-vert avec le code que nous avons exécuté, scéna…

Lire l'article →
Tests logiciels

Tests d'acceptation utilisateur (UAT) : processus, liste de contrôle et exemples réels

Les tests d'acceptation des utilisateurs sont expliqués : qui les exécute, en quoi ils diffèrent du contrôle qualité, un processus en sept étapes, u…

Lire l'article →
Bases de données et SQL

Explication des valeurs SQL NULL : IS NULL, COALESCE, NULLIF et les pièges à éviter

Les valeurs SQL NULL expliquées avec les requêtes que nous avons exécutées : logique à trois valeurs, IS NULL, COALESCE, NULLIF, comment les agrégat…

Lire l'article →
Bases de données et SQL

Instruction SQL CASE WHEN : syntaxe, exemples réels et erreurs courantes

SQL CASE WHEN expliqué avec les requêtes que nous avons exécutées : formulaires simples et recherchés, ordre des conditions, pivots avec SUM, astuce…

Lire l'article →
Développement logiciel

Qu'est-ce que JWT ? Les jetons Web JSON expliqués avec un exemple fonctionnel

Qu’est-ce que JWT ? JSON Web Tokens expliqué : les trois parties, un jeton construit et falsifié dans le code que nous avons exécuté, JWT vs session…

Lire l'article →
Développement logiciel

Boucle d'événements JavaScript expliquée : pile d'appels, microtâches et minuteries avec des exemples

La boucle d'événements JavaScript expliquée avec le code que nous avons exécuté : pile d'appels, files d'attente de tâches et de microtâches, pourqu…

Lire l'article →
Développement logiciel

Générateurs Python et rendement expliqués : économisez de la mémoire avec des exemples réels

Les générateurs Python et le rendement expliqués avec le code que nous avons exécuté : comment ils s'arrêtent et reprennent, les économies de mémoir…

Lire l'article →
Intelligence artificielle

Que sont les intégrations ? Vecteurs et similarité cosinus expliqués simplement

Que sont les intégrations ? Un guide en langage simple sur les vecteurs, la similarité cosinus et la recherche sémantique, avec un petit exemple que…

Lire l'article →
DevOps

Déploiement Bleu-Vert vs Canary : différences, compromis et comment choisir

Explication du déploiement bleu-vert ou canari : comment chacun fonctionne, coût, risque et restauration, regroupement d'utilisateurs stable que nou…

Lire l'article →
Automatisation des tests

Tests instables : quelles sont leurs causes et comment les corriger définitivement

Des tests irréguliers expliqués avec des démos que nous avons exécutées : les principales causes, comment en confirmer et en diagnostiquer une, les …

Lire l'article →
Tests logiciels

Tests de charge vs tests de contrainte : différences, métriques et exemples k6

Explication des tests de charge et des tests de stress : ce que chacun prouve, les tests de pointe et de trempage, les métriques qui comptent, pourq…

Lire l'article →
Tests logiciels

Scénario de test et scénario de test : principales différences, exemples et quand les utiliser

Scénario de test et scénario de test expliqués : des définitions claires, un tableau côte à côte, une fonctionnalité de réinitialisation de mot de p…

Lire l'article →
Bases de données et SQL

Optimisation des requêtes SQL : lire les plans d'exécution et corriger les requêtes lentes, avec des délais

Optimisation des requêtes SQL testée sur un million de lignes : lire les plans d'exécution, ajouter les bons index, corriger les filtres non Sargabl…

Lire l'article →
Bases de données et SQL

SQL UNION vs UNION ALL : différences, exemples et performances testées

SQL UNION vs UNION ALL expliqué avec les requêtes que nous avons exécutées : des doublons, les règles suivent toutes les deux, un véritable bug de d…

Lire l'article →
Développement logiciel

Tutoriel API JavaScript Fetch : GET, POST, gestion des erreurs et délais d'attente

Un didacticiel de l'API JavaScript Fetch testé sur un serveur local : GET et POST, pourquoi 404 ne lance pas, un assistant réutilisable, des chaînes…

Lire l'article →
Développement logiciel

Gestion des fichiers Python : lecture, écriture, CSV, JSON et pathlib avec exemples

Gestion des fichiers Python expliquée avec le code que nous avons exécuté : modes ouverts, avec lecture de fichiers volumineux, CSV, JSON, pathlib, …

Lire l'article →
Développement logiciel

Classes et objets Python : didacticiel POO avec des exemples réels

Classes et objets Python expliqués avec le code que nous avons exécuté : __init__, méthodes, attributs de classe par rapport à l'instance, héritage,…

Lire l'article →
Intelligence artificielle

Qu'est-ce qu'un grand modèle de langage ? Comment fonctionnent les LLM, expliqués sans battage médiatique

Qu’est-ce qu’un grand modèle de langage ? Les LLM expliqués en langage simple : jetons, intégrations, attention, formation, fenêtres de température …

Lire l'article →
DevOps

Commandes Linux pour les débutants : le guide du terminal essentiel avec des exemples

Commandes Linux pour débutants, regroupées par tâche : navigation, fichiers, affichage des journaux, grep, tuyaux, autorisations, processus et archi…

Lire l'article →
Automatisation des tests

Selenium vs Playwright : principales différences, code comparé et lequel choisir

Comparatif Selenium vs Playwright : architecture, attente, support du navigateur et du langage, débogage, même test dans les deux outils, conseils d…

Lire l'article →
Tests logiciels

Partitionnement d'équivalence : technique de conception de tests expliquée avec des exemples concrets

Le partitionnement d'équivalence expliqué : comment trouver des partitions valides et invalides, des exemples concrets d'âges, de mots de passe et d…

Lire l'article →
Tests logiciels

Gravité vs priorité dans les tests logiciels : différences, matrice et exemples réels

Explication de la gravité et de la priorité dans les tests logiciels : des définitions claires, une matrice à quatre voies avec des exemples, des éc…

Lire l'article →
Bases de données et SQL

Tutoriel sur les sous-requêtes SQL : types, sous-requêtes corrélées et EXISTE avec des exemples

Un didacticiel sur les sous-requêtes SQL avec les requêtes que nous avons exécutées : les sous-requêtes scalaires, IN, FROM et SELECT, les sous-requ…

Lire l'article →
Bases de données et SQL

Transactions SQL et ACID expliquées : COMMIT, ROLLBACK et niveaux d'isolement

Transactions SQL et ACID expliquées avec un virement bancaire que nous avons effectué : COMMIT, ROLLBACK, points de sauvegarde, verrous, niveaux d'i…

Lire l'article →
Développement logiciel

Git merge vs rebase : différences, exemples et quand utiliser chacun

Git merge vs rebase expliqué avec de vraies sorties de log, un tableau comparatif, la règle d'or du rebase et un flux de travail sûr pour les équipes.

Lire l'article →
Développement logiciel

List comprehension Python : syntaxe, exemples et cas à éviter

La list comprehension Python expliquée : syntaxe, filtres, if-else, boucles imbriquées, temps mesurés et cas où une boucle for classique est préféra…

Lire l'article →
Bases de données et SQL

Jointures SQL expliquées avec exemples (Inner, Left, Right, Full)

Comprenez les jointures SQL pas à pas avec des tables et requêtes d'exemple : INNER, LEFT, RIGHT, FULL, CROSS et SELF, plus erreurs courantes et con…

Lire l'article →