Quelle est la différence entre une jointure et une requête imbriquée  ?

Par :

,le

En SQL, croiser des données issues de plusieurs tables se fait principalement de deux manières : via une jointure ou via une subquery. Ces deux techniques répondent à des besoins distincts et leur mécanisme d’exécution interne diffère profondément. Une jointure assemble les lignes de plusieurs tables en une seule opération de fusion horizontale. Une requête imbriquée produit un résultat intermédiaire qu’une instruction externe exploite comme entrée de traitement. Maîtriser cette distinction vous permet d’écrire un code SQL plus adapté, plus lisible et mieux optimisé. Le tableau ci-dessous synthétise les critères essentiels pour orienter ce choix de façon claire et rapide.

CritèreJointureRequête imbriquée (subquery)
Objectif principalFusionner plusieurs tablesFiltrer ou calculer dynamiquement
Cas d’usage typiqueFusion de plusieurs tablesFiltrage ou valeur dynamique
Accès aux colonnes des deux tablesOui, directementNon, table secondaire non exposée
Performance sur grands volumesGénéralement optimiséeVariable selon corrélation
Lisibilité généraleCompacte et structuréeIntuitive pour les filtres simples
Lisibilité pour débutantPlus compacte mais densePlus intuitive à structurer
TestabilitéNécessite de lire l’ensembleIsolable et testable seule
Compatibilité avec EXISTSNonOui
Imbrication multipleNon applicableDéconseillée au-delà de 2 niveaux
Remplacement mutuelPas toujours possiblePas toujours possible
Alternative si complexité élevéeCTE (WITH)CTE (WITH)

Qu’est-ce qu’une jointure SQL ?

Une jointure relie deux tables sur une condition de correspondance entre une clé primaire et une clé étrangère. Le moteur SQL parcourt simultanément les deux tables et assemble les lignes qui satisfont la condition définie dans la clause ON. On distingue trois variantes principales : INNER JOINLEFT JOIN et RIGHT JOIN, chacune ayant un périmètre de données différent.

On relie ici la table clients à la table commandes pour afficher chaque commande avec le nom du client associé.

SELECT c.nom, c.prenom, co.date_commande, co.montant
FROM clients c
INNER JOIN commandes co ON c.id_client = co.id_client;

Le INNER JOIN ne retient que les clients ayant au moins une entrée correspondante dans la table commandes. Les colonnes des deux tables sont directement accessibles dans le SELECT sans aucune étape intermédiaire. Remplacez INNER JOIN par LEFT JOIN pour inclure les clients n’ayant passé aucune commande enregistrée.

Quelle est la différence de lisibilité entre les deux approches ?

La requête de jointure est plus compact et naturellement expressive pour décrire une relation entre deux entités de la base. Il devient cependant dense et difficile à lire dès que plusieurs tables sont enchaînées dans la même instruction. La sous-requête segmente la logique en étapes distinctes, ce qui facilite la lecture séquentielle pour un développeur junior. Elle peut néanmoins devenir confuse si plusieurs niveaux d’imbrication se superposent dans le même bloc SQL. Dans ce cas précis, une CTE déclarée avec WITH offre la meilleure lisibilité sans sacrifier la structure logique.

Voici la même logique exprimée avec les deux approches pour mettre en évidence la différence de structure syntaxique.

-- Approche JOIN
SELECT c.nom
FROM clients c
INNER JOIN commandes co ON c.id_client = co.id_client;

-- Approche sous-requête
SELECT nom
FROM clients
WHERE id_client IN (SELECT id_client FROM commandes);

Sur cet exemple , la sous-requête est légèrement plus courte et son intention est immédiatement lisible. La jointure prend tout son avantage dès que vous devez projeter des colonnes issues des deux tables dans le résultat. Choisir l’une ou l’autre approche pour la lisibilité dépend donc du nombre de colonnes affichées dans la requête de sélection finale.

Une requête imbriquée est-elle plus lente qu’une jointure SQL?

La réponse dépend directement du type de sous-requête utilisée et non de la syntaxe elle-même. Une requête non corrélée s’exécute une seule fois et produit un résultat fixe transmis à la requête principale : son coût est comparable à celui d’un JOIN bien indexé. Une subquery corrélée s’exécute une fois par ligne traitée par la requête externe, ce qui génère autant d’opérations que de lignes dans la table. Sur un volume de 100 000 enregistrements, cette mécanique peut provoquer une dégradation sévère des temps de réponse. Analysez systématiquement le plan d’exécution avec la commande EXPLAIN avant de déployer une requête imbriquée corrélée en production.

Voici la même logique écrite avec une subquery corrélée puis avec son équivalent JOIN plus performant.

-- Subquery corrélée dans SELECT (coûteuse)
SELECT cl.nom,
    (SELECT COUNT(*) FROM commandes co WHERE co.id_client = cl.id_client) AS nb_commandes
FROM clients cl;

-- Équivalent optimisé avec LEFT JOIN et GROUP BY
SELECT cl.nom, COUNT(co.id_commande) AS nb_commandes
FROM clients cl
LEFT JOIN commandes co ON cl.id_client = co.id_client
GROUP BY cl.id_client, cl.nom;

Le LEFT JOIN avec GROUP BY parcourt les deux tables en une seule passe et agrège les résultats en mémoire. La subquery corrélée répète son comptage individuellement pour chaque client, ce qui multiplie les lectures disque. Sur de petits jeux de données, l’écart reste imperceptible ; sur de grands volumes, il devient critique.

Quand utiliser une jointure plutôt qu’une sous-requête ?

Optez pour un JOIN dès que vous avez besoin d’afficher des colonnes appartenant à plusieurs tables dans votre résultat. Il est également plus adapté quand vous devez appliquer un groupement sur des données croisées entre plusieurs entités. Privilégiez un LEFT JOIN pour identifier des enregistrements sans correspondance, comme détecter des clients sans commande. Le jointure reste aussi la solution la plus efficace quand les tables impliquées sont volumineuses et correctement indexées sur leurs clés de jointure. En règle générale, commencez par envisager un JOIN et n’optez pour une subquery que si la logique l’exige vraiment.

Peut-on toujours remplacer une sous-requête par une jointure ?

Non, certaines constructions SQL ne peuvent pas être réécrites avec un JOIN sans altérer le résultat ou la logique. L’opérateur EXISTS couplé à une subquery corrélée teste l’existence d’une ligne liée sans exposer ses colonnes, ce qu’un JOIN ne peut pas reproduire exactement. Une sous-requête scalaire dans SELECT retourne une valeur calculée par ligne sans dupliquer les enregistrements de la table principale. Un JOIN sur une table contenant plusieurs correspondances peut au contraire multiplier les lignes retournées de façon non voulue. Dans ces situations spécifiques, la sous-requête ou une CTE est techniquement plus correcte qu’un JOIN équivalent.

Sources: MySQL, Postgres, SqlServer