MySQL wählt eine Spalte DISTINCT mit den entsprechenden anderen Spalten aus

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

Ich möchte DISTINCTErgebnisse aus der FirstNameSpalte auswählen , benötige aber das entsprechende IDund LastName.

Die Ergebnismenge muss nur eine anzeigen John, jedoch eine IDvon 1 und eine LastNamevon Doe.

Herr
quelle
1
Sie möchten den Nachnamen der niedrigsten ID mit einem eindeutigen Vornamen?
Thomas Langston
3
Welche Logik sollte bei der Auswahl der obersten verwendet werden? Ich würde denken, Sie möchten, dass sowohl John Doe als auch John Johnson auftauchen, da sie zwei verschiedene Johns sind, aber das bin nur ich.
Judda
4
DISTINCTist keine Funktion. Alle Antworten mit DISTINCT()sind falsch. Der Fehler wird angezeigt, wenn Sie ihn nicht danach platzieren SELECT.
Frage Überlauf
1
ALL Antworten in Klammern nach dem Wort "eindeutig" sind in der Tat falsch. Distinct ist KEINE Funktion und kann daher keinen Parameter akzeptieren. Die folgenden Klammern werden einfach ignoriert. Es sei denn, Sie verwenden PostgreSQL, bei dem die Klammern einen "komplexen Datentyp" bilden
Used_By_Already

Antworten:

192

Versuchen Sie diese Abfrage

 SELECT ID, FirstName, LastName FROM table GROUP BY(FirstName)
diEcho
quelle
15
Woher wissen wir, welche Zeile zurückgegeben wird?
William Entriken
26
@Full Decent können Sie laut MySQL- Dokumentation nicht : "Der Server kann einen beliebigen Wert aus jeder Gruppe auswählen. Wenn sie nicht identisch sind, sind die ausgewählten Werte unbestimmt." In der Praxis habe ich diese Art von Abfragen erfolgreich mit der ORDER BY-Klausel verwendet. Sie können beispielsweise die ORDER BY-ID ASC / DESC hinzufügen, und MySQL gibt bei jeder Ausführung der Abfrage konsistente Ergebnisse zurück. Aber ich wäre mir sicher, ob jemand undokumentierte Funktionen in der Produktionsumgebung verwenden sollte.
Arunas Junevicius
2
OP erwähnt keine MySQL-Version.
DiEcho
2
@sinaza siehe meine aktualisierte Antwort für MySQL 5.7.5+für die geänderte GROUP BYBehandlung
fyrye
3
Dies funktioniert nicht im Modus only_full_group_by, da weder ID noch Nachname weder aggregiert noch Teil der Gruppierungsfunktion sind. Hilfe!
Ihodonald
63

Das DISTINCTSchlüsselwort funktioniert nicht so, wie Sie es erwarten. Wenn Sie verwenden SELECT DISTINCT col1, col2, col3, wählen Sie tatsächlich alle eindeutigen {col1, col2, col3} Tupel aus.

Brian Driscoll
quelle
14
Vielen Dank, dass Sie Brian darauf hingewiesen haben. Können Sie ein Beispiel geben, wie ich GROUP BY verwenden könnte, um die gleichen Ergebnisse zu erzielen?
Herr
59

Um zu vermeiden , potenziell Ergebnisse unerwartet bei Verwendung GROUP BYohne Aggregatfunktion, wie sie in der akzeptierte Antwort verwendet wird , weil MySQL ist kostenlos abrufen ANY Wert innerhalb des Datensatzes gruppiert werden , wenn nicht eine Aggregatfunktion mit [sic] und Probleme mit ONLY_FULL_GROUP_BY. Bitte erwägen Sie die Verwendung eines Ausschluss-Joins.

Ausschluss beitreten - Eindeutige Entitäten

Angenommen, der Vor- und Nachname sind eindeutig indiziert (eindeutig) , besteht eine Alternative dazu GROUP BYdarin, LEFT JOINdie Ergebnismenge mit a zu filtern, was auch als Ausschluss-JOIN bezeichnet wird.

Siehe Demonstration

Aufsteigende Reihenfolge (AZ)

Abrufen des nach Nachnamen geordneten eindeutigen Vornamens von AZ

Abfrage

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;

Ergebnis

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

Absteigende Reihenfolge (ZA)

So rufen Sie den nach Nachnamen geordneten eindeutigen Vornamen von ZA ab

Abfrage

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;

Ergebnis

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

Sie können die resultierenden Daten dann wie gewünscht bestellen.


Ausschluss beitreten - Mehrdeutige Entitäten

Wenn die Kombination aus Vor- und Nachname nicht eindeutig (mehrdeutig) ist und Sie mehrere Zeilen mit denselben Werten haben, können Sie die Ergebnismenge filtern, indem Sie eine ODER-Bedingung in die JOIN-Kriterien aufnehmen, um auch nach ID zu filtern.

Siehe Demonstration

Tabellenname Daten

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

Abfrage

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;

Ergebnis

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

Bestellte Unterabfrage

BEARBEITEN

Meine ursprüngliche Antwort unter Verwendung einer geordneten Unterabfrage wurde vor MySQL 5.7.5 geschrieben , was aufgrund der Änderungen mit nicht mehr anwendbar ist ONLY_FULL_GROUP_BY. Bitte verwenden Sie stattdessen die obigen Beispiele für Ausschlussverknüpfungen.

Es ist auch wichtig zu beachten; wenn ONLY_FULL_GROUP_BYdeaktiviert ist (ursprüngliche Verhalten vor MySQL 5.7.5) , die Verwendung von GROUP BYohne einer Aggregatfunktion zu unerwarteten Ergebnissen führen, weil MySQL frei zu wählen , ist ANY - Wert innerhalb des Datensatzes gruppiert sind [sic] .

Dies bedeutet, dass ein IDoder lastname-Wert abgerufen werden kann , der der abgerufenen firstnameZeile nicht zugeordnet ist.


WARNUNG

Mit MySQL werden GROUP BYbei Verwendung mit MySQL möglicherweise nicht die erwarteten Ergebnisse erzieltORDER BY

Siehe Testfallbeispiel

Die beste Implementierungsmethode, um die erwarteten Ergebnisse sicherzustellen, besteht darin, den Ergebnismengenbereich mithilfe einer geordneten Unterabfrage zu filtern.

Tabellenname Daten

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

Abfrage

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

Ergebnis

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

Vergleich

Demonstration der unerwarteten Ergebnisse bei Verwendung GROUP BYin Kombination mitORDER BY

Abfrage

SELECT * FROM table_name GROUP BY FirstName ORDER BY ID DESC

Ergebnis

| ID | first |  last |
|----|-------|-------|
|  2 |  Bugs | Bunny |
|  1 |  John |   Doe |
fyrye
quelle
3
Die mit Abstand vollständigste Antwort. Wenn Sie in der ersten Abfrage 'ID desc' in 'ID asc' ändern, können Sie entweder 'John Doe' oder 'John Johnson' abrufen. Das Ändern von 'ID desc' in der zweiten Abfrage hat diesen Effekt nicht.
Carla
Auf Postgres benötigen Sie eine ID in der Gruppe, da Sie sich bei MySQL nicht sicher sind.
Sachin Prasad
Funktioniert eine GROUP BY-Spalte-A ORDER BY-Spalte-B in einer SELECT-Anweisung mit der neuesten Version von MyriaDB immer korrekt?
Neal Davis
@NealDavis Gemäß MariaDB- Handbuch Ordering is done after grouping., also Nein, nicht in diesem Anwendungsfall. Außerdem ignoriert MariaDB ORDER BY in Unterabfragen (gemäß SQL-Standard) ohne a LIMIT. Sie würden wollen , eine verwenden Window FunctionKlärung Weitere Sie Ihre Frage im stellen sollten DBA Stack , da dies eine Frage zu MySQL beziehen ist
fyrye
1
@NateS Nein, der GROUP BYkann einen beliebigen Wert innerhalb des gruppierten Datensatzes auswählen, es sei denn, für diese Spalten wird eine Aggregatfunktion verwendet, um einen bestimmten Wert zu erzwingen. Also lastnameoder idkann aus einer der bestellten Zeilen kommen. Das ursprüngliche Unterabfragebeispiel war standardmäßig akzeptabel, MySQL <= 5.7.4leidet aber technisch immer noch unter dem Problem. Das ORDER BYhilft zwar, eine zufällige Auswahl zu verhindern, ist aber theoretisch immer noch möglich, jedoch mit einer wesentlich geringeren Wahrscheinlichkeit als ohne Verwendung der ORDER BYUnterabfrage.
Fyrye
23
SELECT ID,LastName 
From TABLE_NAME 
GROUP BY FirstName 
HAVING COUNT(*) >=1
Sarath
quelle
2
Durch das Hinzufügen HAVINGwurde meine Abfrage um 50% langsamer.
Buttle Butkus
Gibt es einen Fall, in dem HAVING COUNT (*)> = 1 falsch ist?
Angelos Makrygiorgos
3
SELECT firstName, ID, LastName from tableName GROUP BY firstName
Nanhe Kumar
quelle
3

Wie wäre es mit

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

Sie sind sich nicht sicher, ob Sie dies mit MySQL tun können, aber Sie können einen CTE in T-SQL verwenden

; 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

Andernfalls müssen Sie möglicherweise eine temporäre Tabelle verwenden.

Thomas Langston
quelle
1

Wie von fyrye hervorgehoben , bezieht sich die akzeptierte Antwort auf ältere Versionen von MySQL, die ONLY_FULL_GROUP_BYnoch nicht eingeführt wurden. Mit MySQL 8.0.17 (in diesem Beispiel verwendet) wird ONLY_FULL_GROUP_BYdie folgende Fehlermeldung angezeigt, sofern Sie nicht deaktivieren :

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

FEHLER 1055 (42000): Ausdruck Nr. 1 der SELECT-Liste befindet sich nicht in der GROUP BY-Klausel und enthält die nicht aggregierte Spalte 'mydatabase.table_name.id', die funktional nicht von den Spalten in der GROUP BY-Klausel abhängig ist. Dies ist nicht kompatibel mit sql_mode = only_full_group_by

Eine Möglichkeit, dies zu umgehen , wird von fyrye nicht erwähnt , aber in https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html beschrieben , besteht darin, die ANY_VALUE()Funktion auf die Spalten anzuwenden , die sich befinden nicht in der GROUP BYKlausel ( idund lastNamein diesem Beispiel):

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)

Wie in den oben genannten Dokumenten geschrieben,

In diesem Fall ignoriert MySQL den Nichtdeterminismus von Adresswerten innerhalb jeder Namensgruppe und akzeptiert die Abfrage. Dies kann nützlich sein, wenn es Ihnen einfach egal ist, welcher Wert einer nicht aggregierten Spalte für jede Gruppe ausgewählt wird. ANY_VALUE()ist im Gegensatz zu Funktionen wie SUM()oder keine aggregierte Funktion COUNT(). Es dient lediglich dazu, den Test auf Nichtdeterminismus zu unterdrücken.

Kurt Peek
quelle
Zur Verdeutlichung habe ich es ausdrücklich vermieden, Vorschläge zu machen, ANY_VALUE()da meine Antworten und Kommentare darauf abzielen, mehrdeutige und unvorhersehbare Ergebnismengen zu verhindern. Wie der Funktionsname schon sagt, kann dies dazu führen, dass ein beliebiger Wert aus den ausgewählten Zeilen abgerufen wird. Ich würde mit vorschlagen MAXoder MINstatt.
Fyrye
0

Denken Sie daran, wenn Sie die Gruppe nach und nach dieser Reihenfolge verwenden. MySQL ist die EINZIGE Datenbank, in der Spalten in der Gruppe nach und / oder nach Stück sortiert verwendet werden können, die nicht Teil der select-Anweisung sind.

Beispiel: Wählen Sie Spalte1 aus der Tabellengruppe nach Spalte2 und Reihenfolge nach Spalte3 aus

In anderen Datenbanken wie Postgres, Oracle, MSSQL usw. funktioniert das nicht. In diesen Datenbanken müssten Sie Folgendes tun

Wählen Sie Spalte1, Spalte2, Spalte3 aus der Tabellengruppe nach Spalte2 und Reihenfolge nach Spalte3

Nur einige Informationen für den Fall, dass Sie Ihren aktuellen Code jemals in eine andere Datenbank migrieren oder in einer anderen Datenbank arbeiten und versuchen, Code wiederzuverwenden.

Antonio Delacruz
quelle
-2

Sie können group by verwenden, um unterschiedliche Werte und entsprechende Felder anzuzeigen.

select * from tabel_name group by FirstName

Jetzt haben Sie folgende Ausgabe erhalten:

ID    FirstName     LastName
2     Bugs          Bunny
1     John          Doe


Wenn du gerne antworten möchtest

ID    FirstName     LastName
1     John          Doe
2     Bugs          Bunny

Verwenden Sie dann diese Abfrage.

select * from table_name group by FirstName order by ID
John
quelle
2
Dies wird nicht immer zu erwarteten Ergebnissen führen, wenn eine Gruppierung mit der Bestellung von
fyrye
-3
SELECT DISTINCT(firstName), ID, LastName from tableName GROUP BY firstName

Wäre die beste Wahl IMO

Monty
quelle
32
Dies funktioniert nicht, es werden auch die ID und der Nachname in die eindeutige Bewertung einbezogen.
Ludo - Off the record
2
Dies ist das gleiche wie DISTINCT (Vorname, ID, Nachname)
Tom Taylor
-4
SELECT DISTINCT (column1), column2
FROM table1
GROUP BY column1
Mack
quelle
1
DISTINCT()ist keine Funktion. Auch DISTINCT und GROUP BY machen dasselbe, also ohne Grund beide.
Marki555
Dies ist keine effiziente Anweisung. Sie sollten entweder DISTINCT oder Group By verwenden, nicht beide.
Heshanlk