%% nav-debut : généré par gen_navigation.py, ne pas modifier à la main %% > [!nav] > ↩ [[Code et Développement/Développement d'Applications/SQL IV/SQL IV|Accueil · SQL IV]] > > | [[Code et Développement/Développement d'Applications/SQL IV/03_Combiner deux résultats - UNION\|← Combiner deux résultats : UNION]] | [[Code et Développement/Développement d'Applications/SQL IV/05_Les sous-requêtes - vue d'ensemble\|Les sous-requêtes : vue d'ensemble →]] | > |:---|---:| %% nav-fin %% [[Code et Développement/Développement d'Applications/SQL IV/03_Combiner deux résultats - UNION|Combiner deux résultats - UNION]] # La jointure complète : `FULL JOIN` ## Introduction Dans le chapitre [[Code et Développement/Développement d'Applications/SQL I/04_Requêtes sur 2 tables|Requêtes sur 2 tables]] de SQL I, nous avons vu trois jointures : `INNER JOIN`, `LEFT JOIN` et `RIGHT JOIN`. Il en manque une : celle qui garde **tous les enregistrements des deux tables**, qu'ils aient une correspondance ou non. C'est la **jointure complète**. ### Objectifs du chapitre À la fin de ce chapitre, vous serez capable de : - Comprendre ce que garde une jointure complète. - Écrire une jointure complète avec `LEFT JOIN`, `UNION` et `RIGHT JOIN`. - Utiliser une jointure complète pour repérer les données « orphelines ». --- ## 1. Les données Nous reprenons les tables *Clients* et *Commandes* (voir le chapitre [[Code et Développement/Développement d'Applications/SQL IV/01_Supprimer les doublons - DISTINCT|Supprimer les doublons : DISTINCT]]), avec **une commande en plus** : la commande 108 a été saisie sans client (`id_client` vaut `NULL`). **Table `Clients`** | id_client | nom_client | ville_client | | --------- | ---------- | ------------ | | 1 | Martin | Paris | | 2 | Dupont | Lyon | | 3 | Bernard | Marseille | | 4 | Petit | Lille | | 5 | Robert | Bordeaux | **Table `Commandes`** | id_commande | id_client | date_commande | montant_total | | ----------- | --------- | ------------- | ------------- | | 101 | 1 | 2023-01-10 | 250.00 | | 102 | 1 | 2023-02-18 | 120.00 | | 103 | 2 | 2023-03-05 | 560.00 | | 104 | 2 | 2023-04-22 | 80.00 | | 105 | 2 | 2023-06-01 | 310.00 | | 106 | 3 | 2023-07-14 | 150.00 | | 107 | 4 | 2023-08-03 | 400.00 | | 108 | NULL | 2023-09-10 | 90.00 | Nous avons donc deux enregistrements « orphelins » : - **Robert**, un client sans commande ; - **la commande 108**, une commande sans client. --- ## 2. Ce que garde chaque jointure | Jointure | Robert (client sans commande) | Commande 108 (commande sans client) | Les 7 autres commandes | | --- | --- | --- | --- | | `Clients INNER JOIN Commandes` | perdu | perdue | gardées | | `Clients LEFT JOIN Commandes` | **gardé** | perdue | gardées | | `Clients RIGHT JOIN Commandes` | perdu | **gardée** | gardées | | **Jointure complète** | **gardé** | **gardée** | gardées | > [!definition] Définition : jointure complète > La jointure complète garde : > - les enregistrements qui ont une correspondance dans les deux tables (comme `INNER JOIN`) ; > - les enregistrements de la table de gauche sans correspondance (comme `LEFT JOIN`) ; > - les enregistrements de la table de droite sans correspondance (comme `RIGHT JOIN`). > > Les champs sans correspondance sont remplis par `NULL`. --- ## 3. Comment l'écrire ? Le langage SQL prévoit le mot clé `FULL JOIN` (ou `FULL OUTER JOIN`). **Mais tous les logiciels ne l'acceptent pas.** Pour que votre requête fonctionne partout, **et en particulier à l'examen**, nous écrirons toujours la jointure complète ainsi : > [!important] La jointure complète, version qui fonctionne partout > ```sql > SELECT ... > FROM TableA LEFT JOIN TableB ON condition > UNION > SELECT ... > FROM TableA RIGHT JOIN TableB ON condition; > ``` > On réunit le résultat du `LEFT JOIN` (qui garde tout A) et celui du `RIGHT JOIN` (qui garde tout B) avec un `UNION` (voir le chapitre [[Code et Développement/Développement d'Applications/SQL IV/03_Combiner deux résultats - UNION|Combiner deux résultats : UNION]]). > > Les deux `SELECT` doivent contenir **les mêmes champs, dans le même ordre**. --- ## 4. Exemple On nous demande : ***tous les clients et toutes les commandes, pour contrôler la base.*** ```sql SELECT C.id_client, C.nom_client, Co.id_commande, Co.montant_total FROM Clients C LEFT JOIN Commandes Co ON C.id_client = Co.id_client UNION SELECT C.id_client, C.nom_client, Co.id_commande, Co.montant_total FROM Clients C RIGHT JOIN Commandes Co ON C.id_client = Co.id_client ORDER BY id_commande; ``` ### Étape 1 : le `LEFT JOIN` (tous les clients) | id_client | nom_client | id_commande | montant_total | | --------- | ---------- | ----------- | ------------- | | 1 | Martin | 101 | 250.00 | | 1 | Martin | 102 | 120.00 | | 2 | Dupont | 103 | 560.00 | | 2 | Dupont | 104 | 80.00 | | 2 | Dupont | 105 | 310.00 | | 3 | Bernard | 106 | 150.00 | | 4 | Petit | 107 | 400.00 | | 5 | Robert | NULL | NULL | ### Étape 2 : le `RIGHT JOIN` (toutes les commandes) | id_client | nom_client | id_commande | montant_total | | --------- | ---------- | ----------- | ------------- | | 1 | Martin | 101 | 250.00 | | 1 | Martin | 102 | 120.00 | | 2 | Dupont | 103 | 560.00 | | 2 | Dupont | 104 | 80.00 | | 2 | Dupont | 105 | 310.00 | | 3 | Bernard | 106 | 150.00 | | 4 | Petit | 107 | 400.00 | | NULL | NULL | 108 | 90.00 | ### Étape 3 : l'`UNION` réunit les deux et supprime les doublons Les 7 commandes avec client sont présentes dans les deux résultats : `UNION` n'en garde qu'un exemplaire. Il reste Robert (venu du `LEFT JOIN`) et la commande 108 (venue du `RIGHT JOIN`). | id_client | nom_client | id_commande | montant_total | | --------- | ---------- | ----------- | ------------- | | 5 | Robert | NULL | NULL | | 1 | Martin | 101 | 250.00 | | 1 | Martin | 102 | 120.00 | | 2 | Dupont | 103 | 560.00 | | 2 | Dupont | 104 | 80.00 | | 2 | Dupont | 105 | 310.00 | | 3 | Bernard | 106 | 150.00 | | 4 | Petit | 107 | 400.00 | | NULL | NULL | 108 | 90.00 | Nous obtenons 9 lignes. Robert apparaît en premier : avec un tri croissant, les `NULL` sont placés en premier. Un enregistrement correspond ici à **une commande avec son client, ou un client sans commande, ou une commande sans client**. --- ## 5. Deux pièges à éviter > [!warning] Piège n°1 : `UNION ALL` au lieu de `UNION` > Avec `UNION ALL`, les doublons ne sont pas supprimés : les 7 commandes avec client apparaîtraient **deux fois** (16 lignes au lieu de 9). Pour une jointure complète, c'est bien `UNION` qu'il faut. > [!warning] Piège n°2 : oublier les identifiants > `UNION` supprime les lignes **entièrement identiques**. Si le `SELECT` ne contient pas d'identifiant, deux enregistrements différents peuvent devenir identiques… et l'un des deux disparaît. > > Exemple : si Martin avait passé deux commandes de 120 €, la requête suivante n'afficherait qu'**une seule** ligne « Martin, 120.00 » : > ```sql > SELECT C.nom_client, Co.montant_total > FROM Clients C LEFT JOIN Commandes Co ON C.id_client = Co.id_client > UNION > SELECT C.nom_client, Co.montant_total > FROM Clients C RIGHT JOIN Commandes Co ON C.id_client = Co.id_client; > ``` > **Gardez toujours les clés primaires des deux tables** (`id_client`, `id_commande`) dans le `SELECT` d'une jointure complète. --- ## 6. Un usage courant : trouver les orphelins La jointure complète sert souvent à **contrôler la qualité des données** : quels clients n'ont rien commandé ? quelles commandes n'ont pas de client ? On garde alors seulement les lignes sans correspondance, en filtrant **chaque partie** : ```sql SELECT C.id_client, C.nom_client, Co.id_commande, 'client sans commande' AS anomalie FROM Clients C LEFT JOIN Commandes Co ON C.id_client = Co.id_client WHERE Co.id_commande IS NULL UNION SELECT C.id_client, C.nom_client, Co.id_commande, 'commande sans client' FROM Clients C RIGHT JOIN Commandes Co ON C.id_client = Co.id_client WHERE C.id_client IS NULL; ``` | id_client | nom_client | id_commande | anomalie | | --------- | ---------- | ----------- | -------------------- | | 5 | Robert | NULL | client sans commande | | NULL | NULL | 108 | commande sans client | > [!tip] Rappel > Pour tester l'absence de correspondance, on utilise `IS NULL` et jamais `= NULL` (voir le chapitre [[Code et Développement/Développement d'Applications/SQL I/01_ Détails sur les 6 mots clés d'une requête sélection|Détails sur les 6 mots clés d'une requête sélection]]). --- ## 7. Récapitulatif : quelle jointure choisir ? Nous complétons le tableau du chapitre [[Code et Développement/Développement d'Applications/SQL I/04_Requêtes sur 2 tables|Requêtes sur 2 tables]] : | Jointure | Enregistrements gardés | Exemple de question | | --- | --- | --- | | `Clients INNER JOIN Commandes` | Uniquement ceux qui ont une correspondance dans les deux tables | « La liste des commandes avec le nom de leur client » | | `Clients LEFT JOIN Commandes` | Tous ceux de la table de gauche (`Clients`) | « Tous les clients, y compris ceux qui n'ont jamais commandé » | | `Clients RIGHT JOIN Commandes` | Tous ceux de la table de droite (`Commandes`) | « Toutes les commandes, y compris celles dont on ne connaît pas le client » | | **Jointure complète** (`LEFT JOIN` + `UNION` + `RIGHT JOIN`) | **Tous ceux des deux tables** | « Tous les clients et toutes les commandes, même sans correspondance » | > [!tip] La question à se poser > **De quelle table dois-je garder *tous* les enregistrements ?** > - Aucune : `INNER JOIN` > - Celle de gauche : `LEFT JOIN` > - Celle de droite : `RIGHT JOIN` > - **Les deux : jointure complète** --- > [!important] Ce qu'il faut retenir > - La jointure complète garde tous les enregistrements des deux tables ; les champs sans correspondance valent `NULL`. > - On l'écrit toujours : `LEFT JOIN` … `UNION` … `RIGHT JOIN`, avec les mêmes champs dans les deux `SELECT`. > - `UNION` et pas `UNION ALL` ; et on garde les identifiants dans le `SELECT`. Nous avons terminé les compléments de la requête de sélection. Place aux sous-requêtes : [[Code et Développement/Développement d'Applications/SQL IV/05_Les sous-requêtes - vue d'ensemble|Les sous-requêtes : vue d'ensemble]] %% nav-debut : généré par gen_navigation.py, ne pas modifier à la main %% > [!nav] > ↩ [[Code et Développement/Développement d'Applications/SQL IV/SQL IV|Accueil · SQL IV]] > > | [[Code et Développement/Développement d'Applications/SQL IV/03_Combiner deux résultats - UNION\|← Combiner deux résultats : UNION]] | [[Code et Développement/Développement d'Applications/SQL IV/05_Les sous-requêtes - vue d'ensemble\|Les sous-requêtes : vue d'ensemble →]] | > |:---|---:| %% nav-fin %% --- 🗓️ Historique Dernière mise à jour : `30 octobre 2026` Rédigé par : [[Vincent TELLIER]]