L'une des fonctionnalités SQL les plus puissantes est l'opération JOIN, qui fournit un moyen simple et élégant de combiner chaque ligne d'une table avec chaque ligne d'une autre table. Cependant, il peut arriver que nous souhaitions trouver des valeurs d'une table qui ne sont PAS présentes dans une autre table. Comme nous allons le voir dans l'article de blog d'aujourd'hui, les jointures peuvent également être utilisées à cette fin, en incluant un prédicat sur lequel joindre les tables. Connues sous le nom d'anti-jointures, ces jointures peuvent être utiles pour répondre à une variété de questions liées à l'entreprise, telles que :
- Quels sont les clients qui n'ont pas passé de commande ?
- Quels employés n'ont pas été affectés à un service ?
- Quels vendeurs n'ont pas conclu d'affaires cette semaine ?
Ce blog propose une introduction aux différents types d'anti-jointures et à la façon de les écrire en utilisant quelques exemples basés sur la base de données PostgreSQL dvdrental. Nous écrirons et exécuterons les requêtes dans Navicat Premium Lite 17.
Deux types d’anti-jointures
Il y a deux types d’anti-jointures
- anti-jointure gauche : renvoie les lignes de la table de gauche qui n'ont pas de lignes correspondantes dans la table de droite
- anti-jointure droite : renvoie les lignes de la table de droite qui n'ont pas de lignes correspondantes dans la table de gauche
Les lignes renvoyées sont affichées en bleu dans le diagramme ci-dessous :
La section suivante présentera différentes syntaxes que nous pouvons utiliser pour créer une anti-jointure, en utilisant une anti-jointure gauche à titre d’exemple.
Anti-jointure gauche utilisant EXISTS
Supposons que nous voulions trouver tous les acteurs de la base de données dvdrental qui n'ont joué dans aucun film. Malheureusement, SQL n'a pas de syntaxe intégrée pour cette opération, mais nous pouvons l'émuler en utilisant EXISTS, ou, plus précisément, NOT EXISTS. Voici à quoi ressemblerait cette requête :
SELECT * FROM actor a WHERE NOT EXISTS ( SELECT * FROM film_actor fa WHERE a.actor_id = fa.actor_id )
Si nous l'exécutons dans Navicat Premium Lite 17, nous obtenons les résultats suivants :
Attention à NOT IN !
Comme EXISTS et IN sont équivalents, vous pourriez être tenté de conclure que NOT EXISTS et NOT IN sont également équivalents, mais ce n'est pas toujours le cas ! Ils ne sont équivalents que si la table de droite (dans ce cas, film_actor) a une contrainte NOT NULL sur la clé étrangère (l'acteur_id).
Dans ce cas précis, la requête NOT IN renvoie les mêmes résultats en raison de la contrainte NOT NULL sur la colonne actor_id :
Si la colonne actor_id autorisait les valeurs NULL, un ensemble de résultats vide serait renvoyé. Nous pouvons le vérifier à l'aide de la requête suivante :
SELECT * FROM actor WHERE actor_id NOT IN (1, 2, 3, 4, 5, NULL)
La requête ci-dessus ne renvoie aucune ligne car NULL représente une valeur UNKNOWN en SQL. Puisque nous ne pouvons pas être sûrs que actor_id se trouve dans un ensemble de valeurs dont l'une est UNKNOWN, le prédicat entier devient UNKNOWN !
La façon la plus simple d'éviter le danger posé par la syntaxe NOT IN est de s'en tenir à NOT EXISTS. Il ne vaut même pas la peine de parier sur la présence d'une contrainte NOT NULL, car l'administrateur de la base de données pourrait temporairement désactiver la contrainte pour charger des données, ce qui rendrait votre requête inutile dans l'intervalle.
Syntaxe alternative
Comme évoqué dans l'introduction, il est également possible d'effectuer une anti-jointure en utilisant des LEFT et RIGHT JOIN. Pour que cela fonctionne, vous devez ajouter une clause WHERE avec le prédicat IS NULL. Voici la version LEFT JOIN de cette syntaxe :
SELECT a.* FROM actor a LEFT JOIN film_actor fa ON a.actor_id = fa.actor_id WHERE fa.actor_id IS NULL
Sachez simplement que la syntaxe LEFT/RIGHT JOIN peut s'exécuter plus lentement car l'optimiseur de requête ne reconnaît pas qu’il s’agit d’une opération ANTI JOIN.
Conclusion
Dans le blog d'aujourd'hui, nous avons appris à émuler une anti-jointure gauche à l'aide de trois variantes de syntaxe SQL. Parmi celles-ci, NOT EXISTS devrait être votre premier choix car elle communique le mieux l'intention d'une ANTI JOIN et tend à s'exécuter le plus rapidement.
Vous souhaitez essayer Navicat Premium Lite 17 ? Vous pouvez le télécharger pour une période d'essai GRATUITE de 14 jours. Il est disponible pour les systèmes d'exploitation Windows, macOS et Linux.