¿Cómo se obtienen las filas que contienen el valor máximo para cada conjunto agrupado?
He visto algunas variaciones demasiado complicadas en esta pregunta, y ninguna con una buena respuesta. He tratado de armar el ejemplo más simple posible:
Dada una tabla como la siguiente, con columnas de persona, grupo y edad, ¿cómo obtendría la persona más vieja en cada grupo? (Un empate dentro de un grupo debe dar el primer resultado alfabético)
Person | Group | Age
---
Bob | 1 | 32
Jill | 1 | 34
Shawn| 1 | 42
Jake | 2 | 29
Paul | 2 | 36
Laura| 2 | 39
Conjunto de resultados deseado:
Shawn | 1 | 42
Laura | 2 | 39
mysql
sql
greatest-n-per-group
Yarin
fuente
fuente
Respuestas:
Hay una manera súper simple de hacer esto en mysql:
Esto funciona porque en mysql se le permite no agregar columnas que no sean de grupo, en cuyo caso mysql solo devuelve la primera fila. La solución es primero ordenar los datos de manera que para cada grupo la fila que desee sea primero, luego agrupe por las columnas para las que desea el valor.
Evita las subconsultas complicadas que intentan encontrar el
max()
etc., y también los problemas de devolver varias filas cuando hay más de una con el mismo valor máximo (como lo harían las otras respuestas)Nota: Esta es una solución solo para mysql . Todas las demás bases de datos que conozco arrojarán un error de sintaxis SQL con el mensaje "las columnas no agregadas no se enumeran en el grupo por cláusula" o similar. Debido a que esta solución utiliza un comportamiento indocumentado , los más cautelosos pueden incluir una prueba para afirmar que sigue funcionando si una versión futura de MySQL cambia este comportamiento.
Actualización de la versión 5.7:
Desde la versión 5.7, la
sql-mode
configuración incluyeONLY_FULL_GROUP_BY
por defecto, por lo que para que esto funcione se debe no tener esta opción (editar el archivo de opciones del servidor para eliminar esta configuración).fuente
SELECT
cláusula y no se calcula utilizando una función de agregado.SELECT
cláusula no dependen funcionalmente de lasGROUP BY
columnas. Si está configurado para aceptarlo (`ONLY_FULL_GROUP_BY` está desactivado), funciona como las versiones anteriores (es decir, los valores de esas columnas son indeterminados).GROUP BY
condensa en un registro, pero todos los campos se seleccionarán arbitrariamente de los registros. Se puede ser que actualmente MySQL simplemente siempre recoge la primera fila, pero podría también elegir cualquier otra fila o valores pares de diferentes filas en una versión futura.La solución correcta es:
Cómo funciona:
Hace coincidir cada fila
o
con todas las filas queb
tienen el mismo valor en la columnaGroup
y un valor mayor en la columnaAge
. Cualquier fila queo
no tenga el valor máximo de su grupo en la columnaAge
coincidirá con una o más filas deb
.Esto
LEFT JOIN
hace que coincida con la persona de más edad en el grupo (incluidas las personas que están solas en su grupo) con una fila llena deNULL
s deb
('no hay mayor edad en el grupo').El uso
INNER JOIN
hace que estas filas no coincidan y se ignoran.La
WHERE
cláusula mantiene solo las filas que tienenNULL
s en los campos extraídos deb
. Son las personas más viejas de cada grupo.Lecturas adicionales
Esta solución y muchas otras se explican en el libro Antipatterns de SQL: cómo evitar las trampas de la programación de bases de datos.
fuente
o.Age = b.Age
, por ejemplo, si Paul del grupo 2 está en 39 como Laura. Sin embargo, si no queremos ese comportamiento, podemos hacerlo:ON o.Group = b.Group AND (o.Age < b.Age or (o.Age = b.Age and o.id < b.id))
Puede unirse contra una subconsulta que extrae el
MAX(Group)
yAge
. Este método es portátil en la mayoría de los RDBMS.fuente
Group = 2, Age = 20
, dónde , la subconsulta devolvería una de ellas, pero laON
cláusula de combinación coincidiría con ambas , por lo que obtendría 2 filas con el mismo grupo / edad a través de diferentes valores para las otras columnas, en lugar de unoMi solución simple para SQLite (y probablemente MySQL):
Sin embargo, no funciona en PostgreSQL y quizás en otras plataformas.
En PostgreSQL puede usar la cláusula DISTINCT ON :
fuente
Usando el método de clasificación.
fuente
:=
antes - ¿qué es eso?No estoy seguro si MySQL tiene la función row_number. Si es así, puede usarlo para obtener el resultado deseado. En SQL Server puede hacer algo similar a:
fuente
La solución de axiac es lo que mejor me funcionó al final. Sin embargo, tenía una complejidad adicional: un "valor máximo" calculado, derivado de dos columnas.
Usemos el mismo ejemplo: me gustaría la persona de más edad en cada grupo. Si hay personas que son igualmente viejas, tome la persona más alta.
Tuve que realizar la unión izquierda dos veces para obtener este comportamiento:
¡Espero que esto ayude! Sin embargo, creo que debería haber una mejor manera de hacer esto ...
fuente
Mi solución funciona solo si necesita recuperar solo una columna, sin embargo, para mis necesidades, fue la mejor solución encontrada en términos de rendimiento (¡usa solo una consulta!):
Utiliza GROUP_CONCAT para crear una lista de concat ordenada y luego subcadena solo a la primera.
fuente
Tengo una solución simple usando
WHERE IN
fuente
Uso de CTE: expresiones de tabla comunes:
fuente
En Oracle a continuación, la consulta puede dar el resultado deseado.
fuente
fuente
También puedes probar
fuente
No usaría Grupo como nombre de columna ya que es una palabra reservada. Sin embargo, seguir SQL funcionaría.
fuente
Este método tiene la ventaja de permitirle clasificar por una columna diferente y no destruir los otros datos. Es bastante útil en una situación en la que intenta enumerar pedidos con una columna para artículos, enumerando primero los más pesados.
Fuente: http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat
fuente
deja que el nombre de la mesa sea gente
fuente
Si se necesita ID (y todas las coulmns) de mytable
fuente
Así es como obtengo las N filas máximas por grupo en mysql
cómo funciona:
co.country = ci.country
) < 1
3 elementos -) <3co.id < ci.id
Ejemplo completo aquí:
mysql selecciona n valores máximos por grupo
fuente