MySQL sélectionnez une colonne DISTINCT, avec les autres colonnes correspondantes

198
ID   FirstName   LastName
1      John        Doe
2      Bugs        Bunny
3      John        Johnson

Je souhaite sélectionner les DISTINCTrésultats dans la FirstNamecolonne, mais j'ai besoin des fichiers IDet LastName.

Le jeu de résultats doit afficher un seul John, mais avec un IDde 1 et un LastNamede Doe.

Monsieur
la source
1
Vous voulez que le nom appartienne au plus petit ID avec un prénom distinct?
Thomas Langston
3
Quelle est la logique qui devrait entrer dans la sélection du premier? Je pense que vous voudriez que John Doe et John Johnson se présentent car ce sont deux John distincts, mais ce n'est que moi.
judda
4
DISTINCTn'est pas une fonction. Toutes les réponses DISTINCT()sont fausses. L'erreur apparaîtra lorsque vous ne la placerez pas après SELECT.
Question Overflow
1
ALL les réponses utilisant des parenthèses après le mot distinct sont en effet fausses. Distinct n'est PAS une fonction , il ne peut donc pas accepter de paramètre. Les parenthèses suivantes distinctes sont simplement ignorées. Sauf si vous utilisez PostgreSQL où les parenthèses formeront un "type de données complexe"
Used_By_Already

Réponses:

194

essayez cette requête

 SELECT ID, FirstName, LastName FROM table GROUP BY(FirstName)
diEcho
la source
17
Comment savons-nous quelle ligne sera renvoyée?
William Entriken
28
@Full Decent vous ne pouvez pas, selon la documentation MySQL : "Le serveur est libre de choisir n'importe quelle valeur de chaque groupe, donc à moins qu'elles ne soient identiques, les valeurs choisies sont indéterminées.". En pratique, j'ai utilisé avec succès ce type de requêtes avec la clause ORDER BY, par exemple, vous pouvez ajouter ORDER BY id ASC / DESC et MySQL retournerait des résultats cohérents à chaque fois que vous exécuteriez la requête. Mais je serais sûr que quelqu'un devrait utiliser des fonctionnalités non documentées dans l'environnement de production.
Arunas Junevicius
2
OP ne mentionne pas la version mysql.
diEcho
2
@sinaza voir ma réponse mise à jour pour MySQL 5.7.5+pour la GROUP BYmanipulation
Will B.
3
Cela ne fonctionne pas avec le mode only_full_group_by car ni ID ni LastName ne sont ni agrégés ni ne font partie de la fonction de regroupement. Aidez-moi!
ihodonald
65

Le DISTINCTmot-clé ne fonctionne pas vraiment comme vous l'attendez. Lorsque vous utilisez SELECT DISTINCT col1, col2, col3vous sélectionnez en fait tous les tuples uniques {col1, col2, col3}.

Brian Driscoll
la source
14
Merci d'avoir signalé cela Brian. Pouvez-vous donner un exemple de la façon dont je pourrais utiliser GROUP BY pour obtenir les mêmes résultats?
Monsieur
60

Pour éviter des résultats potentiellement inattendus lors de l'utilisation GROUP BYsans fonction d'agrégation, comme cela est utilisé dans la réponse acceptée , car MySQL est libre de récupérer N'IMPORTE QUELLE valeur dans l'ensemble de données groupé lorsqu'il n'utilise pas de fonction d'agrégation [sic] et pose des problèmes avec ONLY_FULL_GROUP_BY. Veuillez envisager d'utiliser une jointure d'exclusion.

Jointure d'exclusion - Entités non ambiguës

En supposant que le prénom et le nom sont indexés de manière unique (sans ambiguïté) , une alternative à GROUP BYest de trier à l'aide de a LEFT JOINpour filtrer le jeu de résultats, autrement connu sous le nom d'exclusion JOIN.

Voir la démonstration

Ordre croissant (AZ)

Pour récupérer le prénom distinct trié par nom de A à Z

Requete

SELECT t1.*
FROM table_name AS t1
LEFT JOIN table_name AS t2
ON t1.firstname = t2.firstname
AND t1.lastname > t2.lastname
WHERE t2.id IS NULL;

Résultat

| id | firstname | lastname |
|----|-----------|----------|
|  2 |      Bugs |    Bunny |
|  1 |      John |      Doe |

Ordre décroissant (ZA)

Pour récupérer le prénom distinct trié par nom de ZA

Requete

SELECT t1.*
FROM table_name AS t1
LEFT JOIN table_name AS t2
ON t1.firstname = t2.firstname
AND t1.lastname < t2.lastname
WHERE t2.id IS NULL;

Résultat

| id | firstname | lastname |
|----|-----------|----------|
|  2 |      Bugs |    Bunny |
|  3 |      John |  Johnson |

Vous pouvez ensuite commander les données résultantes comme vous le souhaitez.


Jointure d'exclusion - Entités ambiguës

Si la combinaison du prénom et du nom n'est pas unique (ambiguë) et que vous avez plusieurs lignes des mêmes valeurs, vous pouvez filtrer le jeu de résultats en incluant une condition OR sur les critères JOIN pour filtrer également par id.

Voir la démonstration

données nom_table

(1, 'John', 'Doe'),
(2, 'Bugs', 'Bunny'),
(3, 'John', 'Johnson'),
(4, 'John', 'Doe'),
(5, 'John', 'Johnson')

Requete

SELECT t1.*
FROM table_name AS t1
LEFT JOIN table_name AS t2
ON t1.firstname = t2.firstname
AND (t1.lastname > t2.lastname
OR (t1.firstname = t1.firstname AND t1.lastname = t2.lastname AND t1.id > t2.id))
WHERE t2.id IS NULL;

Résultat

| id | firstname | lastname |
|----|-----------|----------|
|  1 |      John |      Doe |
|  2 |      Bugs |    Bunny |

Sous-requête ordonnée

ÉDITER

Ma réponse originale utilisant une sous - requête ordonnée a été écrite avant MySQL 5.7.5 , qui n'est plus applicable, en raison des changements avec ONLY_FULL_GROUP_BY. Veuillez plutôt utiliser les exemples de jointure d'exclusion ci-dessus.

Il est également important de noter; lorsque ONLY_FULL_GROUP_BYest désactivé (comportement d'origine avant MySQL 5.7.5) , l'utilisation de GROUP BYsans fonction d'agrégation peut donner des résultats inattendus, car MySQL est libre de choisir N'IMPORTE QUELLE valeur dans l'ensemble de données en cours de regroupement [sic] .

Cela signifie qu'une valeur IDou lastnamepeut être extraite qui n'est pas associée à la firstnameligne récupérée .


ATTENTION

Avec MySQL GROUP BYpeut ne pas donner les résultats attendus lorsqu'il est utilisé avecORDER BY

Voir l'exemple de cas de test

La meilleure méthode de mise en œuvre, pour garantir les résultats attendus, consiste à filtrer la portée de l'ensemble de résultats à l'aide d'une sous-requête ordonnée.

données nom_table

(1, 'John', 'Doe'),
(2, 'Bugs', 'Bunny'),
(3, 'John', 'Johnson')

Requete

SELECT * FROM (
    SELECT * FROM table_name ORDER BY ID DESC
) AS t1
GROUP BY FirstName

Résultat

| ID | first |    last |
|----|-------|---------|
|  2 |  Bugs |   Bunny |
|  3 |  John | Johnson |

Comparaison

Pour démontrer les résultats inattendus lors de l'utilisation GROUP BYen combinaison avecORDER BY

Requete

SELECT * FROM table_name GROUP BY FirstName ORDER BY ID DESC

Résultat

| ID | first |  last |
|----|-------|-------|
|  2 |  Bugs | Bunny |
|  1 |  John |   Doe |
Sera.
la source
3
Réponse de loin la plus complète. Changer «ID desc» en «ID asc» dans la première requête nous permet de récupérer «John Doe» ou «John Johnson». Changer «ID desc» dans la deuxième requête n'a pas cet effet.
carla
Sur postgres, vous avez besoin d'un ID dans le groupe par pas sûr de mysql.
Sachin Prasad
Une colonne GROUP BY-A ORDER BY colonne-B dans une instruction SELECT fonctionnera-t-elle toujours correctement avec la dernière version de MyriaDB?
Neal Davis
@NealDavis Conformément au manuel MariaDBOrdering is done after grouping. , donc Non pas dans ce cas d'utilisation, en plus MariaDB ignore ORDER BY dans les sous-requêtes (selon le standard SQL) sans a LIMIT. Vous voudriez utiliser un Window FunctionPour plus de précisions, vous devriez poser votre question dans le stackexchange DBA , car il s'agit d'une question relative à MySQL
Will B.
1
@NateS Non, le GROUP BYpeut sélectionner n'importe quelle valeur dans l'ensemble de données groupées, sauf si une fonction d'agrégation est utilisée sur ces colonnes pour forcer une valeur spécifique. Donc lastnameou idpeut provenir de l'une des lignes ordonnées. L'exemple de sous-requête d'origine était acceptable par défaut dans MySQL <= 5.7.4mais souffre encore techniquement du problème. Bien que l ' ORDER BYaide à empêcher une sélection aléatoire, cela reste théoriquement possible, mais avec une probabilité beaucoup moins élevée que sans utiliser la ORDER BYsous - requête.
Will B.
23
SELECT ID,LastName 
From TABLE_NAME 
GROUP BY FirstName 
HAVING COUNT(*) >=1
sarath
la source
3
l'ajout a HAVINGrendu ma requête 50% plus lente.
Buttle Butkus
Y a-t-il un cas dans lequel HAVING COUNT (*)> = 1 sera faux?
Angelos Makrygiorgos
3
SELECT firstName, ID, LastName from tableName GROUP BY firstName
Nanhe Kumar
la source
3

Que diriez-vous

`SELECT 
    my_distinct_column,
    max(col1),
    max(col2),
    max(col3)
    ...
 FROM
    my_table 
 GROUP BY 
    my_distinct_column`
onlinebaba
la source
2

Je ne sais pas si vous pouvez le faire avec MySQL, mais vous pouvez utiliser un CTE dans T-SQL

; WITH tmpPeople AS (
 SELECT 
   DISTINCT(FirstName),
   MIN(Id)      
 FROM People
)
SELECT
 tP.Id,
 tP.FirstName,
 P.LastName
FROM tmpPeople tP
JOIN People P ON tP.Id = P.Id

Sinon, vous devrez peut-être utiliser une table temporaire.

Thomas Langston
la source
2

Comme l'a souligné fyrye , la réponse acceptée concerne les anciennes versions de MySQL qui ONLY_FULL_GROUP_BYn'avaient pas encore été introduites. Avec MySQL 8.0.17 (utilisé dans cet exemple), à ​​moins que vous ne désactiviez, ONLY_FULL_GROUP_BYvous obtiendrez le message d'erreur suivant:

mysql> SELECT id, firstName, lastName FROM table_name GROUP BY firstName;

ERREUR 1055 (42000): L'expression n ° 1 de la liste SELECT n'est pas dans la clause GROUP BY et contient une colonne non agrégée «mydatabase.table_name.id» qui ne dépend pas fonctionnellement des colonnes de la clause GROUP BY; ceci est incompatible avec sql_mode = only_full_group_by

Une façon de contourner ce problème , non mentionnée par fyrye , mais décrite dans https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html , consiste à appliquer la ANY_VALUE()fonction aux colonnes qui sont pas dans la GROUP BYclause ( idet lastNamedans cet exemple):

mysql> SELECT ANY_VALUE(id) as id, firstName, ANY_VALUE(lastName) as lastName FROM table_name GROUP BY firstName;
+----+-----------+----------+
| id | firstName | lastName |
+----+-----------+----------+
|  1 | John      | Doe      |
|  2 | Bugs      | Bunny    |
+----+-----------+----------+
2 rows in set (0.01 sec)

Comme indiqué dans les documents susmentionnés,

Dans ce cas, MySQL ignore le non-déterminisme des valeurs d'adresse dans chaque groupe de noms et accepte la requête. Cela peut être utile si vous ne vous souciez tout simplement pas de la valeur d'une colonne non agrégée choisie pour chaque groupe. ANY_VALUE()n'est pas une fonction d'agrégation, contrairement à des fonctions telles que SUM()ou COUNT(). Il agit simplement pour supprimer le test du non-déterminisme.

Kurt Peek
la source
Pour clarifier, j'ai spécifiquement évité de suggérer d'utiliser ANY_VALUE()car ma réponse et mes commentaires sont axés sur la prévention des ensembles de résultats ambigus et imprévisibles. Comme le nom de la fonction l'indique, cela peut entraîner la récupération de n'importe quelle valeur des lignes sélectionnées. Je suggérerais d'utiliser MAXou à la MINplace.
Will B.
0

Gardez à l'esprit lorsque vous utilisez le group by et order by that MySQL est la SEULE base de données qui permet d'utiliser des colonnes dans le group by et / ou order by piece qui ne font pas partie de l'instruction select.

Par exemple: sélectionnez colonne1 dans le tableau groupe par colonne2 ordre par colonne3

Cela ne volera pas dans d'autres bases de données comme Postgres, Oracle, MSSQL, etc. Vous devrez faire ce qui suit dans ces bases de données

sélectionnez colonne1, colonne2, colonne3 du tableau grouper par colonne2 ordre par colonne3

Juste quelques informations au cas où vous migreriez votre code actuel vers une autre base de données ou commenceriez à travailler dans une autre base de données et essayez de réutiliser le code.

Antonio Delacruz
la source
-2

Vous pouvez utiliser group by pour afficher des valeurs distinctes ainsi que les champs correspondants.

select * from tabel_name group by FirstName

Maintenant, vous avez une sortie comme celle-ci:

ID    FirstName     LastName
2     Bugs          Bunny
1     John          Doe


Si vous voulez répondre comme

ID    FirstName     LastName
1     John          Doe
2     Bugs          Bunny

puis utilisez cette requête,

select * from table_name group by FirstName order by ID
John
la source
2
Cela ne donnera pas toujours les résultats attendus lors du regroupement avec ordre par
Will B.
-3
SELECT DISTINCT(firstName), ID, LastName from tableName GROUP BY firstName

Serait le meilleur pari IMO

Monty
la source
32
cela ne fonctionnera pas, cela prendra également l'ID et le nom de famille dans l'évaluation distincte.
Ludo - Off the record
2
c'est la même chose que DISTINCT (firstName, ID, LastName)
Tom Taylor
-4
SELECT DISTINCT (column1), column2
FROM table1
GROUP BY column1
mack
la source
1
DISTINCT()n'est pas une fonction. DISTINCT et GROUP BY font la même chose, donc aucune raison ne les met tous les deux.
Marki555
Ce n'est pas une déclaration efficace, vous devez utiliser DISTINCT ou Group By pas les deux.
heshanlk