MySQL selecciona una columna DISTINCT, con otras columnas correspondientes


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

Quiero seleccionar DISTINCTresultados de la FirstNamecolumna, pero necesito el correspondiente IDyLastName .

El conjunto de resultados solo debe mostrar uno John, pero con un ID1 y un LastNameDoe.


1
¿Desea que el apellido pertenezca a la ID más baja con un nombre distinto?
Thomas Langston

3
¿Cuál es la lógica que debe entrar en la selección del superior? Creo que querrías que aparezcan John Doe y John Johnson, ya que son dos Johns distintos, pero ese soy yo.
Judda

44
DISTINCTNo es una función. Todas las respuestas con DISTINCT()están mal. El error aparecerá cuando no lo coloques después SELECT.
Desbordamiento de preguntas

1
ALL las respuestas que usan paréntesis después de la palabra distinta son realmente incorrectas. Distinct NO es una función, por lo que no puede aceptar un parámetro. Los paréntesis que siguen a distintos simplemente se ignoran. A menos que esté utilizando PostgreSQL donde los paréntesis formarán un "tipo de datos complejo"
Used_By_Already

Respuestas:


192

prueba esta consulta

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

15
¿Cómo sabemos qué fila se devolverá?
William Entriken

26
@ Decente completo no puede, de acuerdo con la documentación de MySQL : "El servidor es libre de elegir cualquier valor de cada grupo, por lo que, a menos que sean iguales, los valores elegidos son indeterminados". En la práctica, he utilizado con éxito este tipo de consultas con la cláusula ORDER BY, por ejemplo, puede agregar ORDER BY id ASC / DESC y MySQL devolverá resultados consistentes cada vez que ejecute la consulta. Pero estaría seguro de si alguien debería usar características no documentadas en el entorno de producción.
Arunas Junevicius

2
OP no menciona la versión mysql.
diEcho

2
@sinaza vea mi respuesta actualizada para MySQL 5.7.5+para el GROUP BYmanejo
fyrye

3
Esto no funciona con el modo only_full_group_by porque ni ID ni Apellido no son agregados ni forman parte de la función de agrupación. ¡Ayuda!
ihodonald

63

La DISTINCTpalabra clave realmente no funciona de la manera que espera. Cuando lo usa SELECT DISTINCT col1, col2, col3, de hecho, está seleccionando todas las tuplas únicas {col1, col2, col3}.


14
Gracias por señalar esto Brian. ¿Puede proporcionar un ejemplo de cómo podría utilizar GROUP BY para obtener los mismos resultados?
Sr.

59

Para evitar resultados potencialmente inesperados cuando se usa GROUP BYsin una función agregada, como se usa en la respuesta aceptada , porque MySQL es libre de recuperar CUALQUIER valor dentro del conjunto de datos que se agrupa cuando no se usa una función agregada [sic] y problemas conONLY_FULL_GROUP_BY . Considere usar una unión de exclusión.

Exclusion Join - Entidades inequívocas

Suponiendo que el nombre y el apellido están indexados de manera exclusiva (sin ambigüedades) , una alternativa GROUP BYes ordenarlos usando unLEFT JOIN para filtrar el conjunto de resultados, también conocido como exclusión JOIN.

Ver demostración

Orden ascendente (AZ)

Para recuperar el nombre distintivo ordenado por apellido de AZ

Consulta

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 |

Orden descendente (ZA)

Para recuperar el nombre distintivo ordenado por apellido de ZA

Consulta

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 |

Luego puede ordenar los datos resultantes como desee.


Exclusion Join - Entidades ambiguas

Si la combinación de nombre y apellido no es única (ambigua) y tiene varias filas de los mismos valores, puede filtrar el conjunto de resultados al incluir una condición OR en los criterios de UNIR para también filtrar por id.

Ver demostración

datos de nombre_tabla

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

Consulta

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

Mi respuesta original usando una subconsulta ordenada , fue escrita antes de MySQL 5.7.5 , que ya no es aplicable, debido a los cambios conONLY_FULL_GROUP_BY . Utilice los ejemplos de combinación de exclusión anteriores en su lugar.

También es importante tener en cuenta; cuando ONLY_FULL_GROUP_BYestá desactivado (comportamiento original anterior a MySQL 5.7.5) , el uso de GROUP BYsin una función agregada puede producir resultados inesperados, porque MySQL es libre de elegir CUALQUIER valor dentro del conjunto de datos que se está agrupando [sic] .

Esto significa que se puede recuperar un valor IDo que no está asociado con la fila recuperada .lastnamefirstname


ADVERTENCIA

Con MySQL GROUP BYpuede no producir los resultados esperados cuando se usa conORDER BY

Ver ejemplo de caso de prueba

El mejor método de implementación, para garantizar los resultados esperados, es filtrar el alcance del conjunto de resultados utilizando una subconsulta ordenada.

datos de nombre_tabla

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

Consulta

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 |

Comparación

Para demostrar los resultados inesperados cuando se usa GROUP BYen combinación conORDER BY

Consulta

SELECT * FROM table_name GROUP BY FirstName ORDER BY ID DESC

Resultado

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

3
La respuesta más completa con diferencia. Cambiar 'ID desc' a 'ID asc' en la primera consulta nos permite recuperar 'John Doe' o 'John Johnson'. Cambiar 'ID desc' en la segunda consulta no tiene este efecto.
carla

En postgres necesita una identificación en el grupo al no estar seguro de mysql.
Sachin Prasad

¿Una columna GROUP BY-A ORDER BY columna-B en una instrucción SELECT siempre funcionará correctamente con la última versión de MyriaDB?
Neal Davis el

@NealDavis Según el manual de MariaDBOrdering is done after grouping. , entonces No no en este caso de uso, además MariaDB ignora ORDER BY en subconsultas (según el estándar SQL) sin a LIMIT. Desea utilizar un Window FunctionPara obtener más aclaraciones, debe hacer su pregunta en el intercambio de pila DBA , ya que esta es una pregunta relacionada con MySQL
fyrye

1
@NateS No, GROUP BYpueden seleccionar cualquier valor dentro del conjunto de datos agrupados, a menos que se use una función de agregado en esas columnas para forzar un valor específico. Entonces, lastnameo idpuede provenir de cualquiera de las filas ordenadas. El ejemplo de subconsulta original era aceptable por defecto, MySQL <= 5.7.4pero técnicamente aún sufre el problema. Si bien ORDER BYayuda a evitar una selección aleatoria, todavía es teóricamente posible, pero con una probabilidad significativamente menor que sin usar la ORDER BYsubconsulta.
fyrye



3

Qué tal si

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

2

No estoy seguro si puede hacer esto con MySQL, pero puede usar un CTE en 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

De lo contrario, es posible que deba usar una tabla temporal.


1

Como señaló fyrye , la respuesta aceptada corresponde a versiones anteriores de MySQL en las que ONLY_FULL_GROUP_BYaún no se habían introducido. Con MySQL 8.0.17 (usado en este ejemplo), a menos que deshabilite ONLY_FULL_GROUP_BY, recibirá el siguiente mensaje de error:

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

ERROR 1055 (42000): la expresión # 1 de la lista SELECT no está en la cláusula GROUP BY y contiene la columna no agregada 'mydatabase.table_name.id' que no depende funcionalmente de las columnas en la cláusula GROUP BY; esto es incompatible con sql_mode = only_full_group_by

Una forma de evitar esto no mencionada por fyrye , pero descrita en https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html , es aplicar la ANY_VALUE()función a las columnas que están no en la GROUP BYcláusula ( idy lastNameen este ejemplo):

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)

Como está escrito en los documentos antes mencionados,

En este caso, MySQL ignora el no determinismo de los valores de dirección dentro de cada grupo de nombres y acepta la consulta. Esto puede ser útil si simplemente no le importa qué valor de una columna no agregada se elige para cada grupo. ANY_VALUE()no es una función agregada, a diferencia de funciones como SUM()o COUNT(). Simplemente actúa para suprimir la prueba de no determinismo.


Para aclarar, evité específicamente sugerir que ANY_VALUE()mi respuesta y mis comentarios se centren en evitar conjuntos de resultados ambiguos e impredecibles. Dado que, como sugiere el nombre de la función, podría recuperarse cualquier valor de las filas seleccionadas. Sugeriría usar MAXo en su MINlugar.
fyrye

0

Tenga en cuenta al usar el grupo por y ordenar por que MySQL es la ÚNICA base de datos que permite que las columnas se usen en el grupo por y / o ordenar por pieza que no son parte de la instrucción select.

Entonces, por ejemplo: seleccione la columna1 del grupo de tablas por columna2, ordene por columna3

Eso no volará en otras bases de datos como Postgres, Oracle, MSSQL, etc. Debería hacer lo siguiente en esas bases de datos

seleccione column1, column2, column3 del grupo de tablas por column2 ordene por column3

Solo información en caso de que alguna vez migre su código actual a otra base de datos o comience a trabajar en otra base de datos e intente reutilizar el código.


-2

Puede usar group by para mostrar valores distintos y también los campos correspondientes.

select * from tabel_name group by FirstName

Ahora tienes una salida como esta:

ID    FirstName     LastName
2     Bugs          Bunny
1     John          Doe


Si quieres responder como

ID    FirstName     LastName
1     John          Doe
2     Bugs          Bunny

luego usa esta consulta,

select * from table_name group by FirstName order by ID

2
Esto no siempre producirá los resultados esperados cuando se agrupa con orden por
fyrye

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

Sería la mejor apuesta de la OMI


32
esto no funcionará, también tomará la identificación y el apellido en la evaluación distinta.
Ludo - Fuera del récord

2
esto es lo mismo que DISTINCT (firstName, ID, LastName)
Tom Taylor

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

1
DISTINCT()No es una función. También DISTINCT y GROUP BY están haciendo lo mismo, así que no hay razón para ponerlos a ambos.
Marki555

Esta no es una declaración eficiente, debe usar DISTINCT o Group By, no ambas.
heshanlk
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.