Retour à la liste des articles Articles
10 minutes de lecture

Comment se débarrasser des doublons dans une JOIN SQL ?

Avez-vous des doublons indésirables dans votre requête SQL JOIN ? Dans cet article, j'aborderai les raisons possibles de l'apparition de doublons après la jonction de tables en SQL et je montrerai comment corriger une requête en fonction de la raison de ces doublons.

Les analystes de données ayant peu d'expérience de Les jointures en SQL rencontrent souvent des doublons indésirables dans l'ensemble des résultats. Il est difficile pour les débutants d'identifier la raison de ces doublons dans les JOINs.

La meilleure façon d'apprendre Les jointures en SQL est de s'exercer. Je recommande le Les jointures en SQL interactif. Il contient plus de 90 exercices qui vous font pratiquer les différents types de JOIN en SQL.

Dans cet article, j'aborderai les problèmes les plus courants conduisant à des doublons dans les résultats des JOIN SQL. Je présenterai également les solutions possibles à ces problèmes courants.

Commençons par un bref aperçu de Les jointures en SQL.

Les jointures en SQL Aperçu

JOIN La jointure est une construction SQL permettant de demander des informations à partir de deux ou plusieurs tables dans la même requête.

Par exemple, supposons que vous ayez une liste des 100 meilleurs films du XXe siècle et que vous souhaitiez la réduire aux films réalisés par des réalisateurs encore en vie. Dans votre table movies vous n'avez pas d'informations détaillées sur les réalisateurs, seulement leurs identifiants. Mais vous avez une table séparée directors séparée, avec l'ID, le nom complet, l'année de naissance et l'année de décès (le cas échéant) de chaque réalisateur.

Dans votre requête, vous pouvez joindre deux tables par l'ID du réalisateur pour obtenir une liste des films réalisés par des réalisateurs actuellement en vie :

SELECT movies.title, directors.full_name
FROM movies
JOIN directors
ON movies.director_id = directors.id
WHERE directors.death_year IS NULL;

Comme vous pouvez le voir, nous spécifions les tables que nous voulons joindre dans les clauses FROM et JOIN. Ensuite, dans la clause ON, nous spécifions les colonnes de chaque table à utiliser pour joindre ces tables. Si vous ne connaissez pas encore Les jointures en SQL, consultez ce guide d'introduction. Voici également un aide-mémoire sur les JOIN SQL avec la syntaxe et des exemples de différents JOIN.

Le SQL JOIN est un outil formidable qui offre une variété d'options au-delà de la simple jointure de deux tables. Si vous n'êtes pas familier avec les types de SQL JOIN, lisez cet article qui les explique avec des illustrations et des exemples. Selon votre cas d'utilisation, vous pouvez choisir INNER JOIN, LEFT JOIN, RIGHT JOIN et FULL JOIN. Vous pouvez même avoir besoin de joindre des tables sans colonne commune ou de joindre plus de deux tables.

Voyons maintenant comment ces différentes jointures peuvent donner lieu à des doublons indésirables.

Quelles sont les raisons des doublons dans Les jointures en SQL?

Il y a de nombreuses raisons possibles pour obtenir des doublons dans le résultat de votre requête SQL JOIN. Je vais passer en revue les 5 principales raisons ; pour chacune d'entre elles, je montrerai un exemple de requête présentant le problème et une requête corrigée pour obtenir un résultat sans doublons.

Commençons par examiner brièvement les données qui seront utilisées pour nos exemples. Imaginons que nous gérions une agence immobilière qui vend des maisons quelque part aux États-Unis. Nous disposons de tables avec agents, customers, et sales. Voir ci-dessous pour savoir quelles données sont stockées dans chaque table.

agents
idfirst_namelast_nameexperience_years
1KateWhite5
2MelissaBrown2
3AlexandrMcGregor3
4SophiaScott3
5StevenBlack1
6MariaScott1

customers
idfirst_namelast_nameemail
11XavieraLopezxaviera111111@gmail.com
12GabrielCumberlygabriel111111@gmail.com
13ElisabethStevenselisabeth111111@gmail.com
14OprahWinfreyoprah111111@gmail.com
15IvanLeeivan111111@gmail.com

sales
idhouse_iddateagent_first_nameagent_last_namecustomer_idprice
10110122021-11-03KateWhite141200000
10221342021-12-06SophiaScott12950000
10310152021-12-10MariaScott13800000
10420132021-12-12AlexandrMcGregor151350000
10521122021-12-12AlexandrMcGregor151450000
10610102022-01-10StevenBlack111500000

Sans plus attendre, passons à nos exemples.

1. Condition ON manquante

Les débutants qui ne connaissent pas Les jointures en SQL se contentent souvent d'énumérer les tables de FROM sans spécifier la condition JOIN lorsqu'ils essaient de combiner des informations provenant de deux tables ou plus. Il s'agit d'une syntaxe valide, et vous n'obtenez donc aucun message d'erreur. Mais le résultat est une jointure croisée avec toutes les lignes d'une table combinées avec toutes les lignes d'une autre table.

Par exemple, supposons que nous voulions obtenir des informations sur le client qui a acheté une maison particulière (ID #2134). Si nous utilisons la requête suivante :

SELECT house_id, first_name, last_name, email
FROM sales, customers
WHERE house_id = 2134;

Voici le résultat que nous obtenons :

house_idfirst_namelast_nameemail
2134XavieraLopezxaviera111111@gmail.com
2134GabrielCumberlygabriel111111@gmail.com
2134ElisabethStevenselisabeth111111@gmail.com
2134OprahWinfreyoprah111111@gmail.com
2134IvanLeeivan111111@gmail.com

Au lieu d'un enregistrement avec le client que nous voulons, nous avons tous nos clients listés dans le jeu de résultats.

Pour corriger la requête, vous avez besoin d'une syntaxe explicite JOIN. Les tables à combiner sont spécifiées dans FROM et JOIN, et la condition de jointure est spécifiée dans la clause ON:

SELECT s.house_id, c.first_name, c.last_name, c.email
FROM sales s
JOIN customers c
ON s.customer_id = c.id
WHERE s.house_id = 2134;

Ici, nous spécifions l'ID client de la table sales pour qu'il corresponde à l'ID du client de la table customers table. Cela nous donne le résultat souhaité :

house_idfirst_namelast_nameemail
2134GabrielCumberlygabriel111111@gmail.com

Vous pourriez spécifier la condition de jointure dans la clause WHERE pour obtenir le même résultat. Mais cela va à l'encontre de l'utilisation prévue de la clauseWHERE. En outre, il y a d'autres avantages à utiliser la syntaxe JOIN plutôt que de lister les tables dans FROM. Consultez cet article pour comprendre pourquoi la syntaxe JOIN est préférable.

2. Utilisation d'une condition ON incomplète

Les lignes non désirées dans le jeu de résultats peuvent provenir de conditions ON incomplètes. Dans certains cas, vous devez joindre des tables par plusieurs colonnes. Dans ces situations, si vous n'utilisez qu'une seule paire de colonnes, cela donne des lignes en double.

Disons que nous voulons voir le niveau d'expérience de l'agent immobilier pour chaque maison vendue. Si nous commençons par joindre les tables sales et agents par le nom de famille de l'agent :

SELECT s.house_id, a.first_name, a.last_name, a.experience_years
FROM sales s
JOIN agents a
ON s.agent_last_name = a.last_name
ORDER BY s.house_id;

Voici ce que vous obtenez :

house_idfirst_namelast_nameexperience_years
1010StevenBlack1
1012KateWhite5
1015MariaScott1
1015SophiaScott3
2013AlexandrMcGregor3
2112AlexandrMcGregor3
2134MariaScott1
2134SophiaScott3

Ça n'a pas bien marché. Nous avons deux agents différents avec le nom de famille Scott : Maria et Sophia. Par conséquent, les maisons #1015 et #2134 sont chacune incluses deux fois avec des agents différents.

Pour résoudre cette requête, nous devons joindre les tables sales et agents en utilisant deux paires de colonnes, correspondant au nom de famille et au prénom de l'agent :

SELECT s.house_id, a.first_name, a.last_name, a.experience_years
FROM sales s
JOIN agents a
ON s.agent_last_name = a.last_name
   AND s.agent_first_name = a.first_name
ORDER BY s.house_id;

Et voici le résultat que nous recherchions.

house_idfirst_namelast_nameexperience_years
1010StevenBlack1
1012KateWhite5
1015MariaScott1
2013AlexandrMcGregor3
2112AlexandrMcGregor3
2134SophiaScott3

Bien que JOIN soit l'un des outils de base de SQL, vous devez être conscient des nombreuses nuances pour joindre des tables efficacement. Je vous recommande de vous entraîner sur Les jointures en SQL avec ce cours interactif qui couvre une variété de scénarios de jointure avec 93 défis de codage.

3. Sélection d'un sous-ensemble de colonnes

Dans certains cas, les enregistrements du jeu de résultats ne sont pas des doublons mais semblent l'être parce que le sous-ensemble de colonnes sélectionné ne montre pas toutes les différences entre les enregistrements.

Par exemple, imaginons que nous voulions voir les dates auxquelles chaque agent immobilier a vendu une maison. Si nous utilisons la requête suivante :

SELECT a.first_name, a.last_name, s.date
FROM agents a
JOIN sales s
ON s.agent_last_name = a.last_name
   AND s.agent_first_name = a.first_name;

elle produit le résultat suivant :

first_namelast_namedate
KateWhite2021-11-03
SophiaScott2021-12-06
AlexandrMcGregor2021-12-12
AlexandrMcGregor2021-12-12
MariaScott2021-12-10
StevenBlack2022-01-10

L'ensemble de résultats comprend deux enregistrements avec Alexandr McGregor qui semblent identiques. Cependant, si vous ajoutez l'ID de la maison à la déclaration SELECT, vous voyez que ces deux enregistrements correspondent à la vente de deux maisons différentes le même jour.

Si vous n'êtes pas intéressé par ces informations supplémentaires et que vous souhaitez qu'une seule ligne soit affichée ici, utilisez DISTINCT:

SELECT DISTINCT a.first_name, a.last_name, s.date
FROM agents a
JOIN sales s
ON s.agent_last_name = a.last_name
   AND s.agent_first_name = a.first_name;

Maintenant, le résultat est le suivant :

first_namelast_namedate
KateWhite2021-11-03
SophiaScott2021-12-06
AlexandrMcGregor2021-12-12
MariaScott2021-12-10
StevenBlack2022-01-10

4. Affichage des lignes correspondantes uniquement

Un problème similaire peut survenir si vous souhaitez ne répertorier que les lignes d'une table, mais qu'il existe plusieurs enregistrements correspondants dans l'autre table. Vous vous retrouvez alors avec des doublons indésirables dans votre ensemble de résultats.

Par exemple, disons que nous voulons lister tous les clients qui ont acheté des maisons par l'intermédiaire de notre agence. Si nous utilisons la requête suivante :

SELECT c.first_name, c.last_name, c.email
FROM customers c
JOIN sales s
ON c.id = s.customer_id;

voici le résultat :

first_namelast_nameemail
GabrielCumberlygabriel111111@gmail.com
ElisabethStevenselisabeth111111@gmail.com
XavieraLopezxaviera111111@gmail.com
OprahWinfreyoprah111111@gmail.com
IvanLeeivan111111@gmail.com
IvanLeeivan111111@gmail.com

Comme vous le voyez, le tableau résultant inclut Ivan Lee deux fois. Cela est dû au fait qu'il a acheté deux maisons et qu'il y a deux enregistrements correspondants dans la table sales dans la table. Une solution possible est d'utiliser DISTINCT comme dans l'exemple précédent. Une solution encore meilleure est d'éviter d'utiliser SQL JOIN en filtrant le jeu de résultats à l'aide du mot-clé EXISTS:

SELECT c.first_name, c.last_name, c.email
FROM customers c
WHERE EXISTS (SELECT customer_id FROM sales);

Maintenant, le résultat est :

first_namelast_nameemail
GabrielCumberlygabriel111111@gmail.com
ElisabethStevenselisabeth111111@gmail.com
XavieraLopezxaviera111111@gmail.com
OprahWinfreyoprah111111@gmail.com
IvanLeeivan111111@gmail.com

Cela vous donne le résultat souhaité et rend également l'intention de votre requête plus claire.

5. Utilisation des jointures automatiques

Enfin, les doublons indésirables dans les jointures résultent souvent d'une spécification incorrecte des conditions de jointure dans les jointures automatiques, c'est-à-dire lorsqu'une table est jointe à elle-même.

Disons que nous voulons que nos agents forment des paires pour notre prochaine formation. Évidemment, nous ne voulons pas qu'un agent soit jumelé avec lui-même. Nous pourrions donc spécifier la condition ON a1.id <> a2.id :

SELECT
    a1.first_name as agent1_first_name,
    a1.last_name as agent1_last_name,
    a1.experience_years as agent1_experience,
    a2.first_name as agent2_first_name,
    a2.last_name as agent2_last_name,
    a2.experience_years as agent2_experience
FROM agents a1
JOIN agents a2
ON a1.id <> a2.id
ORDER BY a1.id;

Cependant, cette requête produit chaque paire deux fois. Par exemple, dans la première ligne du tableau ci-dessous, Kate White est considérée comme l'agent 1, et Maria Scott comme l'agent 2. Mais plus près de la fin du tableau, vous obtenez la même paire d'agents mais avec Maria Scott comme agent 1 et Kate White comme agent 2.

agent1_first_nameagent1_last_nameagent1_experienceagent2_first_nameagent2_last_nameagent2_experience
KateWhite5MariaScott1
KateWhite5StevenBlack1
KateWhite5MelissaBrown2
KateWhite5SophiaScott3
KateWhite5AlexandrMcGregor3
MelissaBrown2StevenBlack1
MelissaBrown2SophiaScott3
MelissaBrown2MariaScott1
MelissaBrown2AlexandrMcGregor3
MelissaBrown2KateWhite5
AlexandrMcGregor3MariaScott1
AlexandrMcGregor3MelissaBrown2
AlexandrMcGregor3SophiaScott3
AlexandrMcGregor3KateWhite5
AlexandrMcGregor3StevenBlack1
SophiaScott3MariaScott1
SophiaScott3StevenBlack1
SophiaScott3AlexandrMcGregor3
SophiaScott3MelissaBrown2
SophiaScott3KateWhite5
StevenBlack1SophiaScott3
StevenBlack1AlexandrMcGregor3
StevenBlack1MariaScott1
StevenBlack1MelissaBrown2
StevenBlack1KateWhite5
MariaScott1KateWhite5
MariaScott1AlexandrMcGregor3
MariaScott1SophiaScott3
MariaScott1StevenBlack1
MariaScott1MelissaBrown2

Pour résoudre ce problème, vous devez ajouter une condition explicite pour inclure chaque paire une seule fois. Une solution courante consiste à spécifier la condition de jonction a1.id < a2.id. Avec cette condition, vous obtenez la paire Kate White et Maria Scott, mais pas l'inverse. En effet, l'identifiant de Kate (1) est un nombre inférieur à celui de Maria (6).

Dans la pratique, vous pouvez avoir d'autres conditions pour apparier les agents. Par exemple, vous pouvez vouloir jumeler des agents plus expérimentés (3+ ans) avec des agents moins expérimentés (< 3 ans). La condition de filtrage correspondante dans WHERE résout le problème :

SELECT
    a1.first_name as agent1_first_name,
    a1.last_name as agent1_last_name,
    a1.experience_years as agent1_experience,
    a2.first_name as agent2_first_name,
    a2.last_name as agent2_last_name,
    a2.experience_years as agent2_experience
FROM agents a1
JOIN agents a2
ON a1.id <> a2.id
WHERE a1.experience_years>=3 AND a2.experience_years < 3
ORDER BY a1.id;

Voici le résultat :

agent1_first_nameagent1_last_nameagent1_experienceagent2_first_nameagent2_last_nameagent2_experience
KateWhite5StevenBlack1
KateWhite5MelissaBrown2
KateWhite5MariaScott1
AlexandrMcGregor3MariaScott1
AlexandrMcGregor3StevenBlack1
AlexandrMcGregor3MelissaBrown2
SophiaScott3MariaScott1
SophiaScott3StevenBlack1
SophiaScott3MelissaBrown2

Cet ensemble de résultats semble bien meilleur et permet de sélectionner plus facilement trois paires, chacune composée d'un agent plus expérimenté et d'un autre moins expérimenté.

Pratiquons Les jointures en SQL!

Joindre des tables en SQL n'est pas si difficile. Mais cela demande beaucoup de pratique. Si vous voulez éviter les pièges tels que les doublons indésirables dans les JOIN et les enregistrements manquants, suivez ce guide sur la pratique de Les jointures en SQL.

Si vous n'avez qu'une expérience de base de SQL et que vous souhaitez combiner des données provenant de plusieurs tables avec plus d'assurance, je vous recommande ce Les jointures en SQL cours interactif. Il couvre tous les principaux types de jointures, ainsi que la jointure d'une table avec elle-même, la jointure de plusieurs tables dans une requête et la jointure de tables sur des colonnes non clés. Vous trouverez plus de détails sur ce cours dans cet article de présentation.

Bonus. Voici les 10 meilleures questions d'entretien SQL JOIN avec les réponses.

Merci de votre lecture et bon apprentissage !