MySQL seleziona una colonna DISTINCT, con altre colonne corrispondenti


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

Voglio selezionare i DISTINCTrisultati dalla FirstNamecolonna, ma ho bisogno del corrispondente IDe LastName.

Il set di risultati deve mostrare solo uno John, ma con un IDdi 1 e un LastNamedi Doe.


1
Vuoi che il cognome appartenga all'ID più basso con un nome distinto?
Thomas Langston,

3
Qual è la logica che dovrebbe andare nella selezione di quella in alto? Penserei che vorresti che John Doe e John Johnson si presentassero dal momento che sono due John distinti, ma sono solo io.
judda,

4
DISTINCTnon è una funzione. Tutte le risposte con DISTINCT()sono sbagliate. L'errore verrà visualizzato quando non lo si inserisce dopo SELECT.
Domanda Overflow del

1
ALL le risposte usando le parentesi dopo la parola distinta sono effettivamente sbagliate. Distinto NON è una funzione, quindi non può accettare un parametro. Le parentesi che seguono distinte vengono semplicemente ignorate. A meno che tu non stia usando PostgreSQL in cui le parentesi formeranno un "tipo di dati complesso"
Used_By_Al già

Risposte:


192

prova questa query

 SELECT ID, FirstName, LastName FROM table GROUP BY(FirstName)

16
Come facciamo a sapere quale riga verrà restituita?
William Entriken,

27
@Full Decent non è possibile, secondo la documentazione di MySQL : "Il server è libero di scegliere qualsiasi valore da ciascun gruppo, quindi a meno che non siano gli stessi, i valori scelti sono indeterminati". In pratica ho usato con successo questo tipo di query con la clausola ORDER BY, ad esempio potresti aggiungere ORDER BY id ASC / DESC e MySQL restituirebbe risultati coerenti ogni volta che eseguiresti la query. Ma sarei sicuro se qualcuno dovrebbe usare funzionalità non documentate nell'ambiente di produzione.
Arunas Junevicius,

2
OP non menziona la versione mysql.
diEcho l'

2
@sinaza vedi la mia risposta aggiornata per MySQL 5.7.5+per la GROUP BYgestione
fyrye

3
Questo non funziona con la modalità only_full_group_by perché né ID né LastName non sono né aggregati né parte della funzione di raggruppamento. Aiuto!
Ihodonald,

64

La DISTINCTparola chiave non funziona davvero nel modo previsto. Quando lo usi, SELECT DISTINCT col1, col2, col3stai infatti selezionando tutte le tuple {col1, col2, col3} uniche.


14
Grazie per averlo segnalato Brian. Potete fornire un esempio di come potrei utilizzare GROUP BY per ottenere gli stessi risultati?
sig.

59

Per evitare risultati potenzialmente imprevisti quando si utilizza GROUP BYsenza una funzione di aggregazione, come viene utilizzato nella risposta accettata , poiché MySQL è libero di recuperare QUALSIASI valore all'interno del set di dati da raggruppare quando non si utilizza una funzione di aggregazione [sic] e problemi conONLY_FULL_GROUP_BY . Prendi in considerazione l'utilizzo di un join di esclusione.

Partecipazione di esclusione - Entità non ambigue

Supponendo che il nome e il cognome siano indicizzati in modo univoco (inequivocabile) , un'alternativa a GROUP BYè quella di ordinare utilizzando a LEFT JOINper filtrare il set di risultati, altrimenti noto come JOIN di esclusione.

Vedi dimostrazione

Ordine crescente (AZ)

Per recuperare il nome distinto ordinato per cognome dalla A alla Z.

domanda

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;

Risultato

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

Ordine decrescente (ZA)

Per recuperare il nome distinto ordinato per cognome da ZA

domanda

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;

Risultato

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

È quindi possibile ordinare i dati risultanti come desiderato.


Partecipazione di esclusione - Entità ambigue

Se la combinazione di nome e cognome non è univoca (ambigua) e si dispone di più righe con gli stessi valori, è possibile filtrare il set di risultati includendo una condizione OR nei criteri JOIN per filtrare anche per id.

Vedi dimostrazione

dati table_name

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

domanda

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;

Risultato

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

Sottoquery ordinata

MODIFICARE

La mia risposta originale utilizzando una subquery ordinata , è stata scritta prima di MySQL 5.7.5 , che non è più applicabile, a causa delle modifiche conONLY_FULL_GROUP_BY . Utilizzare invece gli esempi di join di esclusione sopra riportati.

È anche importante notare; quando ONLY_FULL_GROUP_BYè disabilitato (comportamento originale prima di MySQL 5.7.5) , l'uso di GROUP BYsenza una funzione aggregata può produrre risultati imprevisti, poiché MySQL è libero di scegliere QUALSIASI valore all'interno del set di dati da raggruppare [sic] .

Significa che è possibile recuperare un valore IDo che non è associato alla riga recuperata .lastnamefirstname


AVVERTIMENTO

Con MySQL GROUP BYpotrebbe non produrre i risultati previsti se utilizzato conORDER BY

Vedi esempio di test case

Il miglior metodo di implementazione, per garantire i risultati previsti, è filtrare l'ambito del set di risultati utilizzando una subquery ordinata.

dati table_name

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

domanda

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

Risultato

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

Confronto

Per dimostrare i risultati imprevisti quando si utilizza GROUP BYin combinazione conORDER BY

domanda

SELECT * FROM table_name GROUP BY FirstName ORDER BY ID DESC

Risultato

| ID | first |  last |
|----|-------|-------|
|  2 |  Bugs | Bunny |
|  1 |  John |   Doe |

3
La risposta più completa di gran lunga. La modifica di "ID desc" in "ID asc" nella prima query ci consente di recuperare "John Doe" o "John Johnson". La modifica di "ID desc" nella seconda query non ha questo effetto.
carla,

Su Postgres hai bisogno di un ID nel gruppo non sicuro di mysql.
Sachin Prasad,

Una colonna GROUP BY-A ORDER BY-colonna B in un'istruzione SELECT funzionerà sempre correttamente con l'ultima versione di MyriaDB?
Neal Davis,

@NealDavis Come da manuale MariaDBOrdering is done after grouping. , quindi No non in questo caso d'uso, inoltre MariaDB ignora ORDER BY nelle sottoquery (secondo lo standard SQL) senza a LIMIT. Vorresti usare un Window FunctionPer maggiori chiarimenti dovresti porre la tua domanda nello scambio DBA dello stack , poiché questa è una domanda relativa a MySQL
fyrye

1
@NateS No, è GROUP BYpossibile selezionare qualsiasi valore all'interno del set di dati raggruppati, a meno che non venga utilizzata una funzione aggregata su quelle colonne per forzare un valore specifico. Quindi lastnameo idpuò provenire da una qualsiasi delle righe ordinate. L'esempio di subquery originale era accettabile per impostazione predefinita in MySQL <= 5.7.4ma tecnicamente soffre ancora del problema. Mentre ORDER BYaiuta a prevenire una selezione casuale, è ancora teoricamente possibile, ma con una probabilità significativamente inferiore rispetto all'utilizzo della ORDER BYsubquery.
Fyrye


3
SELECT firstName, ID, LastName from tableName GROUP BY firstName

3

Che ne dite di

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

2

Non sono sicuro se puoi farlo con MySQL, ma puoi usare un CTE in 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

Altrimenti potrebbe essere necessario utilizzare una tabella temporanea.


1

Come sottolineato da Fyrye , la risposta accettata riguarda le versioni precedenti di MySQL in cui ONLY_FULL_GROUP_BYnon era ancora stata introdotta. Con MySQL 8.0.17 (utilizzato in questo esempio), a meno che tu non lo disabiliti ONLY_FULL_GROUP_BY, visualizzerai il seguente messaggio di errore:

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

ERRORE 1055 (42000): l'espressione n. 1 dell'elenco SELECT non è nella clausola GROUP BY e contiene la colonna non aggregata 'mydatabase.table_name.id' che non dipende funzionalmente dalle colonne nella clausola GROUP BY; questo è incompatibile con sql_mode = only_full_group_by

Un modo per aggirare questo problema non menzionato da Fyrye , ma descritto in https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html , è applicare la ANY_VALUE()funzione alle colonne che sono non nella GROUP BYclausola ( ide lastNamein questo esempio):

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)

Come scritto nei suddetti documenti,

In questo caso, MySQL ignora il non determinismo dei valori dell'indirizzo all'interno di ciascun gruppo di nomi e accetta la query. Questo può essere utile se semplicemente non ti interessa quale valore di una colonna non aggregata viene scelto per ciascun gruppo. ANY_VALUE()non è una funzione aggregata, a differenza di funzioni come SUM()o COUNT(). Agisce semplicemente per sopprimere il test del non determinismo.


Per chiarimenti, ho espressamente evitato di suggerire di utilizzare ANY_VALUE()poiché la mia risposta e i miei commenti sono focalizzati sulla prevenzione di insiemi di risultati ambigui e imprevedibili. Poiché, come suggerisce il nome della funzione, potrebbe essere recuperato qualsiasi valore dalle righe selezionate. Suggerirei di utilizzare MAXo MINinvece.
fyrye,

0

Tenere presente che quando si utilizza il gruppo e si ordina in base al quale MySQL è l'UNICO database che consente alle colonne di essere utilizzate nel gruppo da e / o ordina per pezzo che non fanno parte dell'istruzione select.

Quindi, per esempio: selezionare colonna1 dal gruppo di tabelle per colonna2 ordina per colonna3

Quello non volerà in altri database come Postgres, Oracle, MSSQL, ecc. Dovresti fare quanto segue in quei database

seleziona colonna1, colonna2, colonna3 dal gruppo di tabelle per colonna2 ordina per colonna3

Solo alcune informazioni nel caso in cui dovessi mai migrare il tuo codice corrente su un altro database o iniziare a lavorare in un altro database e provare a riutilizzare il codice.


-2

È possibile utilizzare group by per visualizzare valori distinti e anche campi corrispondenti.

select * from tabel_name group by FirstName

Ora hai un output in questo modo:

ID    FirstName     LastName
2     Bugs          Bunny
1     John          Doe


Se vuoi rispondere come

ID    FirstName     LastName
1     John          Doe
2     Bugs          Bunny

quindi usa questa query,

select * from table_name group by FirstName order by ID

2
Questo non sempre rendimento atteso i risultati quando il raggruppamento con ordine da
fyrye

-3
SELECT DISTINCT(firstName), ID, LastName from tableName GROUP BY firstName

Sarebbe la migliore scommessa IMO


32
questo non funzionerà, prenderà anche l'ID e il cognome nella valutazione distinta.
Ludo: non registrato il

2
questo è lo stesso di DISTINCT (nome, ID, cognome)
Tom Taylor

-4
SELECT DISTINCT (column1), column2
FROM table1
GROUP BY column1

1
DISTINCT()non è una funzione. Anche DISTINCT e GROUP BY stanno facendo la stessa cosa, quindi non c'è motivo di metterli entrambi.
Marki555,

Questa non è un'affermazione efficace, dovresti usare DISTINCT o Raggruppa per non entrambi.
heshanlk,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.