MySQL seleciona uma coluna DISTINCT, com as outras colunas correspondentes

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

Quero selecionar DISTINCTresultados da FirstNamecoluna, mas preciso do correspondente IDe LastName.

O conjunto de resultados precisa mostrar apenas um John, mas com um IDde 1 e um LastNamede Doe.

Sr
fonte
1
Você quer o sobrenome pertencente ao ID mais baixo com um nome distinto?
Thomas Langston
3
Qual é a lógica que deve entrar na seleção da primeira? Eu acho que você gostaria que John Doe e John Johnson aparecessem, uma vez que são dois Johns distintos, mas sou apenas eu.
judda
4
DISTINCTnão é uma função. Todas as respostas com DISTINCT()estão erradas. O erro será exibido quando você não o colocar depois SELECT.
Pergunta Overflow
1
ALL respostas usando parênteses após a palavra distinto estão realmente erradas. Distinct NÃO é uma função, portanto não pode aceitar um parâmetro. Os parênteses após distintos são simplesmente ignorados. A menos que você esteja usando o PostgreSQL, onde os parênteses formarão um "tipo de dados complexo"
Used_By_Already 14/02/16

Respostas:

192

tente esta consulta

 SELECT ID, FirstName, LastName FROM table GROUP BY(FirstName)
diEcho
fonte
15
Como sabemos qual linha será retornada?
William Entriken
26
@ Decente total, você não pode, de acordo com a documentação do MySQL : "O servidor é livre para escolher qualquer valor de cada grupo, portanto, a menos que sejam iguais, os valores escolhidos são indeterminados.". Na prática, usei esse tipo de consulta com sucesso com a cláusula ORDER BY, por exemplo, você poderia adicionar ORDER BY id ASC / DESC e o MySQL retornaria resultados consistentes toda vez que você executasse a consulta. Mas eu teria certeza se alguém deveria usar recursos não documentados no ambiente de produção.
Arunas Junevicius
2
O OP não menciona a versão mysql.
diEcho
2
@sinaza ver minha resposta atualizado para o MySQL 5.7.5+para a mudança de GROUP BYmanuseio
fyrye
3
Isso não funciona no modo only_full_group_by, porque nem o ID nem o Sobrenome não são agregados nem fazem parte da função de agrupamento. Socorro!
Ihodonald
63

A DISTINCTpalavra-chave não funciona da maneira que você espera. Quando você usa, SELECT DISTINCT col1, col2, col3na verdade, seleciona todas as tuplas {col1, col2, col3} exclusivas.

Brian Driscoll
fonte
14
Obrigado por apontar isso, Brian. Você pode fornecer um exemplo de como eu poderia utilizar o GROUP BY para obter os mesmos resultados?
mr
59

Para evitar resultados potencialmente inesperados ao usar GROUP BYsem uma função agregada, como é usado na resposta aceita , porque o MySQL é livre para recuperar QUALQUER valor dentro do conjunto de dados que está sendo agrupado quando não estiver usando uma função agregada [sic] e problemas com ONLY_FULL_GROUP_BY. Considere usar uma associação de exclusão.

Associação de exclusão - entidades não ambíguas

Supondo que o nome e o sobrenome sejam indexados exclusivamente (sem ambiguidade) , uma alternativa GROUP BYé classificar usando a LEFT JOINpara filtrar o conjunto de resultados, também conhecido como exclusão JOIN.

Ver demonstração

Ordem crescente (AZ)

Para recuperar o nome distinto ordenado pelo sobrenome de AZ

Inquerir

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;

Resultado

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

Ordem decrescente (ZA)

Para recuperar o nome distinto ordenado pelo sobrenome do ZA

Inquerir

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;

Resultado

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

Você pode solicitar os dados resultantes conforme desejado.


Junção de exclusão - entidades ambíguas

Se a combinação de nome e sobrenome não for exclusiva (ambígua) e você tiver várias linhas dos mesmos valores, poderá filtrar o conjunto de resultados incluindo uma condição OR nos critérios JOIN para também filtrar por ID.

Ver demonstração

dados table_name

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

Inquerir

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;

Resultado

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

Subconsulta ordenada

EDITAR

Minha resposta original usando uma subconsulta ordenada foi escrita antes do MySQL 5.7.5 , que não é mais aplicável, devido às alterações no ONLY_FULL_GROUP_BY. Em vez disso, use os exemplos de junção de exclusão acima.

Também é importante notar; Quando ONLY_FULL_GROUP_BYestá desabilitado (comportamento original anterior ao MySQL 5.7.5) , o uso de GROUP BYsem uma função agregada pode produzir resultados inesperados, porque o MySQL é livre para escolher QUALQUER valor dentro do conjunto de dados que está sendo agrupado [sic] .

Significa que um valor IDou lastnamepode ser recuperado que não está associado à firstnamelinha recuperada .


AVISO

O MySQL GROUP BYpode não produzir os resultados esperados quando usado comORDER BY

Consulte o Exemplo de Caso de Teste

O melhor método de implementação, para garantir os resultados esperados, é filtrar o escopo do conjunto de resultados usando uma subconsulta ordenada.

dados table_name

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

Inquerir

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

Resultado

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

Comparação

Para demonstrar resultados inesperados ao usar GROUP BYem combinação comORDER BY

Inquerir

SELECT * FROM table_name GROUP BY FirstName ORDER BY ID DESC

Resultado

| ID | first |  last |
|----|-------|-------|
|  2 |  Bugs | Bunny |
|  1 |  John |   Doe |
fyrye
fonte
3
Resposta mais completa de longe. Alterar 'ID desc' para 'ID asc' na primeira consulta nos permite recuperar 'John Doe' ou 'John Johnson'. Alterar 'ID desc' na segunda consulta não tem esse efeito.
Carla
No postgres, você precisa de ID no grupo, não tendo certeza do mysql.
Sachin Prasad
Uma coluna GROUP BY-A ORDER BY coluna-B em uma instrução SELECT sempre funcionará corretamente com a versão mais recente do MyriaDB?
Neal Davis
@NealDavis Conforme o manual do MariaDBOrdering is done after grouping. , portanto, não neste caso de uso, o MariaDB ignora ORDER BY em subconsultas (conforme o padrão SQL) sem a LIMIT. Você iria querer usar um Window FunctionPara mais esclarecimentos, você deve fazer sua pergunta no Stackexchange DBA , pois esta é uma questão relativa ao MySQL
fyrye
1
@NateS Não, é GROUP BYpossível selecionar qualquer valor no conjunto de dados agrupados, a menos que uma função agregada seja usada nessas colunas para forçar um valor específico. Portanto, lastnameou idpode vir de qualquer uma das linhas ordenadas. O exemplo de subconsulta original era aceitável por padrão, MySQL <= 5.7.4mas tecnicamente ainda sofre com o problema. Enquanto ORDER BYisso ajuda a impedir uma seleção aleatória, ainda é teoricamente possível, mas com uma probabilidade significativamente menor do que sem o uso da ORDER BYsubconsulta.
fyrye
23
SELECT ID,LastName 
From TABLE_NAME 
GROUP BY FirstName 
HAVING COUNT(*) >=1
sarath
fonte
2
adicionar HAVINGfez minha consulta 50% mais lenta.
Buttle Butkus
Existe algum caso em que HAVING COUNT (*)> = 1 será falso?
Angelos Makrygiorgos
3
SELECT firstName, ID, LastName from tableName GROUP BY firstName
Nanhe Kumar
fonte
3

E se

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

Não tenho certeza se você pode fazer isso com o MySQL, mas você pode usar um CTE no 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

Caso contrário, talvez você precise usar uma tabela temporária.

Thomas Langston
fonte
1

Como apontado por fyrye , a resposta aceita refere-se a versões mais antigas do MySQL nas quais ONLY_FULL_GROUP_BYainda não haviam sido introduzidas. Com o MySQL 8.0.17 (usado neste exemplo), a menos que você desabilite, ONLY_FULL_GROUP_BYvocê receberá a seguinte mensagem de erro:

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

ERRO 1055 (42000): A expressão nº 1 da lista SELECT não está na cláusula GROUP BY e contém a coluna não agregada 'mydatabase.table_name.id', que não é funcionalmente dependente das colunas na cláusula GROUP BY; isso é incompatível com sql_mode = only_full_group_by

Uma maneira de contornar isso não mencionada por fyrye , mas descrita em https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html , é aplicar a ANY_VALUE()função às colunas que são não na GROUP BYcláusula ( ide lastNameneste exemplo):

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)

Conforme escrito nos documentos acima mencionados,

Nesse caso, o MySQL ignora o não-determinismo dos valores de endereço dentro de cada grupo de nomes e aceita a consulta. Isso pode ser útil se você simplesmente não se importar com o valor de uma coluna não agregada escolhida para cada grupo. ANY_VALUE()não é uma função agregada, diferentemente de funções como SUM()ou COUNT(). Ele simplesmente atua para suprimir o teste de não-determinismo.

Kurt Peek
fonte
Para esclarecimento, evitei sugerir especificamente o uso, ANY_VALUE()pois minhas respostas e comentários estão focados na prevenção de conjuntos de resultados ambíguos e imprevisíveis. Como o nome da função sugere, pode resultar na recuperação de qualquer valor das linhas selecionadas. Eu sugeriria usar MAXou em MINvez disso.
fyrye
0

Lembre-se de que ao usar o grupo por e ordenar, o MySQL é o ÚNICO banco de dados que permite que colunas sejam usadas no grupo por e / ou ordem por parte que não fazem parte da instrução select.

Por exemplo: selecione coluna1 do grupo de tabelas por coluna2 e ordene por coluna3

Isso não voará em outros bancos de dados como Postgres, Oracle, MSSQL, etc. Você precisaria fazer o seguinte nesses bancos de dados

selecione coluna1, coluna2, coluna3 do grupo de tabelas por coluna2, ordem por coluna3

Apenas algumas informações, caso você migre seu código atual para outro banco de dados ou comece a trabalhar em outro banco de dados e tente reutilizar o código.

Antonio Delacruz
fonte
-2

Você pode usar agrupar por para exibir valores distintos e também campos correspondentes.

select * from tabel_name group by FirstName

Agora você tem uma saída como esta:

ID    FirstName     LastName
2     Bugs          Bunny
1     John          Doe


Se você quiser responder como

ID    FirstName     LastName
1     John          Doe
2     Bugs          Bunny

então use esta consulta,

select * from table_name group by FirstName order by ID
John
fonte
2
Este não será sempre rendimento esperado resultados quando o agrupamento com a ordem por
fyrye
-3
SELECT DISTINCT(firstName), ID, LastName from tableName GROUP BY firstName

Seria a melhor aposta IMO

Monty
fonte
32
isso não funcionará, também levará o ID e o sobrenome para a avaliação distinta.
Ludo - Fora de registro
2
este é o mesmo que distintas (firstName, ID, LastName)
Tom Taylor
-4
SELECT DISTINCT (column1), column2
FROM table1
GROUP BY column1
mack
fonte
1
DISTINCT()não é uma função. DISTINCT e GROUP BY também estão fazendo a mesma coisa, então não há razão para colocar os dois.
Marki555
Esta não é uma declaração eficiente, você deve usar DISTINCT ou Group By, não ambos.
heshanlk