%% 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]]