INNER JOIN en SQL: Qu’est-ce qu’une jointure interne ?

Par :

,le

L’opérateur INNER JOIN  en SQL assemble les enregistrements de deux tables en ne conservant que les lignes pour lesquelles la condition de liaison est vérifiée des deux côtés. C’est l’opérateur de jointure le plus utilisé dans les bases de données relationnelles en production.

Syntaxe de INNER JOIN en SQL

La syntaxe est identique sur MySQL, PostgreSQL, SQL Server et MariaDB. Le mot-clé INNER est facultatif — écrire JOIN seul produit un résultat strictement identique. La formulation complète est néanmoins recommandée pour la lisibilité du code en environnement collaboratif.

La clause ON est indissociable de INNER JOIN : elle définit la condition d’égalité entre les deux colonnes de liaison des tables impliquées. Sans elle, le moteur ne peut pas déterminer quelles paires d’enregistrements assembler. La structure respecte toujours l’ordre suivant : 

FROM table_a INNER JOIN table_b ON condition

Dès que deux tables partagent des colonnes portant le même identifiant, chaque référence doit être préfixée pour lever l’ambiguïté. Les alias allègent considérablement l’écriture et améliorent la maintenabilité du code sur des schémas complexes.

-- Sans alias : noms de tables complets explicites
SELECT clients.nom, commandes.produit, commandes.montant
FROM commandes
INNER JOIN clients ON commandes.client_id = clients.id;

-- Avec alias : syntaxe allégée recommandée en pratique
SELECT c.nom, cmd.produit, cmd.montant
FROM commandes AS cmd
INNER JOIN clients AS c ON cmd.client_id = c.id;

Les deux instructions produisent un résultat identique : chaque ligne associe le nom de l’acheteur à son achat correspondant. Seuls les profils ayant au moins une transaction et les achats rattachés à un client existant figurent dans le tableau retourné. Il faut vérifier que client_id ne contient pas de valeurs NULL pour s’assurer qu’aucun enregistrement valide n’est écarté involontairement.

Que signifie jointure interne ?

La jointure interne est un mécanisme permettant de relier deux tables via une colonne commune et écarter automatiquement toute ligne sans équivalent dans la table opposée. Ce comportement d’intersection est précisément ce qui distingue cet opérateur des autres mécanismes de liaison disponibles en SQL. Un enregistrement sans correspondance est silencieusement éliminé du jeu de données retourné.

Cette logique est adaptée aux situations où seules les données complètes et cohérentes présentent un intérêt métier. Un rapport de facturation affichant le nom de l’acheteur pour chaque ligne d’achat est l’illustration la plus représentative. Tout achat sans acheteur identifiable et tout profil sans historique d’achat sont naturellement écartés.

Voici les deux tables de référence utilisées dans l’ensemble des exemples suivants :

-- Clients enregistrés dans la base
CREATE TABLE clients (
    id    INT PRIMARY KEY,
    nom   VARCHAR(100),
    ville VARCHAR(100)
);

-- Achats liés à chaque client
CREATE TABLE commandes (
    id        INT PRIMARY KEY,
    client_id INT,            -- clé étrangère vers clients.id
    produit   VARCHAR(100),
    montant   DECIMAL(10,2)
);

La colonne client_id dans commandes pointe vers id dans clients et constitue le point d’ancrage de toutes les jointures internes de cet article. Une valeur client_id sans correspondance côté clients sera exclue sans avertissement par le moteur SQL. Il faut en tenir compte lors de l’audit des données pour détecter d’éventuelles incohérences référentielles.

Quelle est la différence entre une jointure et une jointure interne ?

Le mot-clé JOIN renvoie implicitement à INNER JOIN pour deux raisons:

  • JOIN est un raccourcis pour ne pas avoir à écrire INNER JOIN
  • L’ INNER JOIN couvre la majorité des besoins d’extraction en production.

Cependant le terme générique « jointure » en français désigne le mécanisme général qui permet d’assembler plusieurs tables au sein d’une même requête SQL, alors qu’une jointure interne est un type de jointure spécifique. Confondre les deux notions est une erreur fréquente chez les développeurs qui débutent avec les bases relationnelles.

La distinction fondamentale porte sur le traitement des enregistrements sans correspondance entre les tables liées. Une jointure interne les exclut systématiquement du résultat, tandis que les jointures externes les conservent en affichant NULL pour les colonnes manquantes. Ce comportement d’exclusion rend INNER JOIN plus restrictif que OUTER JOIN.

Filtrer et trier avec INNER JOIN

INNER JOIN se combine avec WHEREORDER BY et GROUP BY comme n’importe quelle instruction SELECT standard. Les clauses de filtrage s’appliquent après la résolution de la liaison et opèrent sur le jeu de données assemblé. Cette combinaison couvre la majorité des besoins d’extraction en contexte professionnel.

Restreindre les résultats à une zone géographique, une plage de dates ou un seuil financier sont des cas d’usage fréquents dans les rapports métier. Le tri final s’applique sur n’importe quelle colonne du résultat combiné, qu’elle provienne de l’une ou l’autre des tables interrogées.

-- Transactions supérieures à 200 € pour les acheteurs parisiens, classées par montant
SELECT c.nom AS acheteur, cmd.produit, cmd.montant
FROM commandes AS cmd
INNER JOIN clients AS c ON cmd.client_id = c.id
WHERE c.ville = 'Paris'
  AND cmd.montant > 200
ORDER BY cmd.montant DESC;

Cette instruction ne retourne que les achats réalisés par des clients domiciliés à Paris avec un montant supérieur à 200 €, classés du plus élevé au plus faible. Le filtre WHERE s’évalue sur le résultat combiné après application de la jointure interne. Des index sur ville et montant sont recommandés pour maintenir des performances acceptables sur des tables volumineuses.

Comment appliquer une jointure interne à plusieurs tables  ?

Plusieurs jointures internes peuvent s’enchaîner dans une même requête pour assembler trois entités ou davantage. Chaque opérateur étend le résultat précédent en intégrant une nouvelle table via sa propre condition de liaison. L’ordre des jointures n’affecte pas le résultat final mais peut influencer le plan d’exécution choisi par l’optimiseur.

Ce type de requête est courant dans les schémas e-commerce où transactions, acheteurs, références produit, catégories et vendeurs sont stockés dans des tables distinctes.

-- Rapport complet : acheteur, article, catégorie, vendeur et ville de livraison
SELECT
    c.nom          AS acheteur,
    p.libelle      AS article,
    cat.nom        AS categorie,
    v.nom          AS vendeur,
    liv.ville      AS ville_livraison,
    cmd.montant
FROM commandes AS cmd
INNER JOIN clients      AS c   ON cmd.client_id    = c.id
INNER JOIN produits     AS p   ON cmd.produit_id   = p.id
INNER JOIN categories   AS cat ON p.categorie_id   = cat.id
INNER JOIN vendeurs     AS v   ON cmd.vendeur_id   = v.id
INNER JOIN livraisons   AS liv ON cmd.livraison_id = liv.id
WHERE cmd.montant > 50
ORDER BY cmd.montant DESC;

Cette instruction assemble cinq tables en quatre liaisons successives pour produire un rapport métier directement exploitable sans traitement applicatif supplémentaire. Seules les commandes dont chaque référence — acheteur, article, catégorie, vendeur et adresse de livraison — existe dans les tables correspondantes apparaissent dans le résultat. Des index sur client_idproduit_idcategorie_idvendeur_id et livraison_id sont indispensables pour prévenir toute dégradation des performances à l’échelle.

Comment fonctionne une jointure interne s’il existe plusieurs correspondances  ?

Lorsqu’une ligne de la table gauche correspond à plusieurs lignes de la table droite, INNER JOIN retourne autant de lignes dans le résultat que de correspondances trouvées. Ce comportement est intentionnel et reflète fidèlement la cardinalité réelle des données stockées dans la base. Un client ayant passé cinq commandes générera donc cinq lignes distinctes dans le résultat final.

Ce scénario est la norme dans tout schéma relationnel de type un-à-plusieurs : un client peut avoir plusieurs commandes, un produit peut appartenir à plusieurs transactions, un vendeur peut traiter plusieurs achats. Il ne s’agit pas d’un doublon mais d’un résultat structurellement correct. C’est à la couche applicatif ou à une fonction d’agrégation de regrouper ces lignes si nécessaire.

Pour observer ce mécanisme, voici un jeu de données concret avec un client ayant trois commandes distinctes :

-- Données de test : un client, trois commandes associées
INSERT INTO clients VALUES (1, 'Sophie Martin', 'Lyon');

INSERT INTO commandes VALUES (101, 1, 'Clavier', 45.00);
INSERT INTO commandes VALUES (102, 1, 'Écran',   320.00);
INSERT INTO commandes VALUES (103, 1, 'Souris',  25.00);

-- Requête INNER JOIN sur ces données
SELECT c.nom AS acheteur, cmd.produit, cmd.montant
FROM commandes AS cmd
INNER JOIN clients AS c ON cmd.client_id = c.id;

Cette requête retourne trois lignes, toutes rattachées à « Sophie Martin », une pour chaque achat enregistré dans commandes. Le nom de l’acheteur est répété sur chaque ligne car la jointure reconstruit l’association à chaque correspondance trouvée. Pour obtenir une seule ligne par client avec le total de ses achats, il faut combiner INNER JOIN avec GROUP BY et SUM() :

-- Regrouper les achats par acheteur avec le total cumulé
SELECT c.nom AS acheteur, COUNT(cmd.id) AS nb_commandes, SUM(cmd.montant) AS total
FROM commandes AS cmd
INNER JOIN clients AS c ON cmd.client_id = c.id
GROUP BY c.id, c.nom
ORDER BY total DESC;

Cette seconde instruction consolide les trois lignes de Sophie Martin en une seule, avec le nombre de transactions et le montant cumulé. GROUP BY c.id garantit que le regroupement s’effectue par identifiant unique et non par nom, évitant toute fusion accidentelle entre deux clients homonymes. Il faut toujours inclure la clé primaire dans GROUP BY lorsque le nom seul ne suffit pas à distinguer les enregistrements de façon certaine.

Erreurs courantes avec INNER JOIN

Deux erreurs récurrentes affectent les requêtes avec INNER JOIN : l’omission de la clause ON et l’ambiguïté sur les colonnes partagées. Ces deux cas produisent soit un résultat erroné, soit un blocage à l’exécution, souvent difficiles à détecter sans vérification du plan de requête.

-- ❌ Incorrect : clause ON absente, produit cartésien non intentionnel
SELECT c.nom, cmd.produit
FROM commandes AS cmd
INNER JOIN clients AS c;
-- Chaque ligne de "commandes" est combinée avec chaque ligne de "clients".

-- ✅ Correct : condition de liaison explicite sur les colonnes référencées
SELECT c.nom, cmd.produit
FROM commandes AS cmd
INNER JOIN clients AS c ON cmd.client_id = c.id;
-- ❌ Incorrect : colonne "id" non préfixée, ambiguïté entre les deux tables
SELECT id, nom, produit
FROM commandes AS cmd
INNER JOIN clients AS c ON cmd.client_id = c.id;

-- ✅ Correct : chaque colonne préfixée par son alias de table
SELECT cmd.id AS id_commande, c.nom, cmd.produit
FROM commandes AS cmd
INNER JOIN clients AS c ON cmd.client_id = c.id;

L’absence de ON génère un produit cartésien qui multiplie le volume des résultats de façon exponentielle et sollicite inutilement le moteur. L’ambiguïté de colonne provoque une erreur bloquante que le SGBD signale explicitement à l’exécution. Ces deux écueils sont évités en appliquant systématiquement alias et condition ON dès la rédaction de la requête.

Sources: MySql, Postgres, SqlServer