SQL : SELECT DISTINCT

Aujourd’hui, je vous propose un aperçu de la clause « distinct » du langage SQL. Beaucoup de gens voient le distinct dans une requête SQL comme un moyen d’indiquer que l’on veut récupérer que les lignes contenant des valeurs distinctes sur certaines colonnes. Cette affirmation n’est ni totalement fausse, ni totalement vraie. Explication :

La clause « distinct » que l’on écrit juste après l’instruction « select » d’une requête SQL permet de filtrer les lignes en doublons

La définition s’arrête ici. Il n’est pas question de parler de colonnes ou d’ensemble de colonnes dans la description de l’instruction « distinct ». En effet, cette instruction s’applique sur l’intégralité des colonnes demandées à l’instruction select. Nous allons dérouler un petit exemple en requête. Tout d’abord il est nécessaire d’utiliser un jeu de donnée. N’en ayant pas sous la main, je vais en créer un avec une requête SQL et des unions :

Select departement
Select departement

La requête :

select 'FR' as Cd_Pays, '01' as Dep_Num ,'Ain' as Dep_Lib, 'Rhône-Alpes' as Reg_lib
union
select 'FR', '02','Aisne','Picardie'
union
select 'FR', '03','Allier','Auvergne'
union
select 'FR', '04','Alpes-de-Haute-Provence','Provence-Alpes-Côte d Azur'
union
select 'FR', '05','Hautes-Alpes','Provence-Alpes-Côte d Azur'
union
select 'FR', '06','Alpes-Maritimes','Provence-Alpes-Côte d Azur'
union
select 'FR', '07','Ardèche','Rhône-Alpes'
union
select 'FR', '08','Ardennes','Champagne-Ardenne';

Maintenant, imaginons que je veuille récupérer l’intégralité de ma table. Je peux utiliser l’instruction « select * … »

select *
select *

Cette table contient le code pays, le numéro de département, le libelle du département ainsi que le libellé de la région du département. La géographie française nous a appris les cardinalités suivantes :

  • Pays 1:n Région
  • Région 1:n Département

Dans une table ayant la granularité du département, une sélection seulement du code Pays provoquera des doublons. Exemple :

Select Cd_Pays
Select Cd_Pays

Le résultat donne autant de fois le code du pays qu’il a été trouvé dans notre jeu de donnée. Cependant, mon intention est de récupérer tous les pays dans une table et je n’ai besoin que d’une seule fois chaque valeur. Le résultat que j’attends est juste : « FR ». C’est là qu’intervient la clause distinct. En effet, cette clause comme décrite plus haut va servir à supprimer toutes les lignes en doublons. le résultat est donc :

select distinct Cd_Pays
select distinct Cd_Pays

De la même manière, je peux vouloir connaitre l’intégralité des départements présents dans cette table :

select distinct Dep_Lib
select distinct Dep_Lib

La clause distinct fait parfaitement le travail demandé. Maintenant, imaginons que pour la construction d’une liste de valeur j’ai besoin du libelle de la région, mais aussi du code pays ?

select distinct Reg_lib,Cd_Pays
select distinct Reg_lib,Cd_Pays

Cette méthode peut être déclinée à l’infinie avec l’ajout du libelle de département, des villes, quartiers, etc. Dans un cas trivial comme celui d’une hiérarchie géographique connue tel que le découpage territorial, il ne nous viendrait pas à l’idée de sélectionner les colonnes du code pays et du libelle de la région, mais de ne pas accepter les doublons sur la colonne du code pays ? Quel serait le sens de cette requête ? Récupérer le nom de la première région pour chaque pays ? Et cette réflexion peut se décliner à l’infinie en rajoutant des découpages géographiques.

Imaginons maintenant un nouveau cas de figure avec une table contenant le nom, le prénom et le mail de nos utilisateurs. Lors d’une analyse de nos applications, nous avons besoin de faire la liste de tous nos utilisateurs. La requête est facile à faire, nous allons sélectionner les différentes colonnes dans notre table de données :

select nom, prenom,email
select nom, prenom,email

Le résultat n’est pas celui attendu, car nous avons plusieurs fois l’utilisateur « Jean Dupont » avec différentes adresses email. Celà est traité de la sorte dans notre application, l’utilisateur est bien présent 3 fois, il ne s’agit pas d’homonymes. L’envie est donc tenante de faire un distinct sur les colonnes nom et prénom afin de n’avoir qu’une seule fois chaque utilisateur. Seulement la question suivante se pose : « Comment faire un distinct seulement sur les colonnes « nom » et « prénom » de mon select ? » « select distinct (nom, prénom), email » ?

select (nom, prenom),email
select (nom, prenom),email

C’est bien tenté, mais visiblement ça ne marche pas. Dans ce cas de figure, nous sommes souvent tentés de chercher sur internet ou appeler l’expert en base de données pour savoir comment faire un distinct sur seulement certaines colonnes d’un select. D’ailleurs en général on vous demande comment faire un distinct sur certains champs dans un select, mais là je vais vous renvoyer sur un autre article « Colonne Vs Champ« .

En fait, cette demande qui arrive plus souvent qu’on ne le pense ne peut pas se résoudre simplement avec un distinct. Quelle ligne le sgbd est censé supprimer ? Il n’est pas capable de décider pour vous. Il faudra donc lui indiquer de sélectionner une adresse mail en particulier. Cette demande peut être traitée de manières différentes. Dans ce cas de figure, j’utiliserai personnellement une fonction d’agrégation ainsi que la clause « group by » nécessaire lorsque l’on utilise une fonction d’agrégation. Ici, je vais utiliser la fonction d’agrégation min() pour récupérer le premier email par ordre alphabétique :

select nom, prenom,min(email)
select nom, prenom,min(email)
select nom, prenom,min(email)
from (
select 'Jean' as prenom, 'Dupont' as nom ,'jean.dupont@mail.com' as email
union
select 'Jean', 'Dupond','jean.dupond@mail.com'
union
select 'Alfred', 'Robert','alfred.R@mail.com'
union
select 'Jean', 'Dupont','jdupont@mail.com'
union
select 'Jean', 'Dupont','j-dupont@mail.com'
) as Sample
group by nom, prenom

Cette solution n’est bien sur pas la seul ni forcément la meilleur. Il est possible de proposer une règle de gestion pour sélectionner quel e-mail récupérer. Mais ce n’est pas le sujet de l’article.

Ce qu’il est important de retenir c’est que la clause distinct filtre les données sur le résultat final global d’un select.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *