Obtenga los n primeros registros para cada grupo de resultados agrupados [duplicado]
El siguiente es el ejemplo más simple posible, aunque cualquier solución debería poder escalarse a la cantidad de n resultados principales necesarios:
Dada una tabla como la siguiente, con columnas de persona, grupo y edad, ¿cómo obtendrías las 2 personas mayores de cada grupo? (Los empates dentro de los grupos no deben dar más resultados, pero sí dar los 2 primeros en orden alfabético)
+--------+-------+-----+ | Persona | Grupo | Edad | +--------+-------+-----+ | Bob | 1 | 32 | | Jill | 1 | 34 | | Shawn | 1 | 42 | | Jake | 2 | 29 | | Pablo | 2 | 36 | | Laura | 2 | 39 | +--------+-------+-----+
Conjunto de resultados deseado:
+--------+-------+-----+ | Shawn | 1 | 42 | | Jill | 1 | 34 | | Laura | 2 | 39 | | Pablo | 2 | 36 | +--------+-------+-----+
NOTA: Esta pregunta se basa en una anterior: Obtener registros con valor máximo para cada grupo de resultados SQL agrupados , para obtener una única fila superior de cada grupo, y que recibió una excelente respuesta específica de MySQL de @Bohemian:
select *
from (select * from mytable order by `Group`, Age desc, Person) x
group by `Group`
Me encantaría poder aprovechar esto, aunque no veo cómo.
Aquí hay una forma de hacer esto, usando UNION ALL
(consulte SQL Fiddle with Demo ). Esto funciona con dos grupos; si tiene más de dos grupos, deberá especificar el group
número y agregar consultas para cada uno group
:
(
select *
from mytable
where `group` = 1
order by age desc
LIMIT 2
)
UNION ALL
(
select *
from mytable
where `group` = 2
order by age desc
LIMIT 2
)
Hay varias formas de hacer esto; consulte este artículo para determinar la mejor ruta para su situación:
http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/
Editar:
Esto también podría funcionar para usted, genera un número de fila para cada registro. Usando un ejemplo del enlace anterior, esto devolverá solo aquellos registros con un número de fila menor o igual a 2:
select person, `group`, age
from
(
select person, `group`, age,
(@num:=if(@group = `group`, @num +1, if(@group := `group`, 1, 1))) row_number
from test t
CROSS JOIN (select @num:=0, @group:=null) c
order by `Group`, Age desc, person
) as x
where x.row_number <= 2;
Ver demostración
En otras bases de datos puedes hacer esto usando ROW_NUMBER
. MySQL no es compatible ROW_NUMBER
, pero puedes usar variables para emularlo:
SELECT
person,
groupname,
age
FROM
(
SELECT
person,
groupname,
age,
@rn := IF(@prev = groupname, @rn + 1, 1) AS rn,
@prev := groupname
FROM mytable
JOIN (SELECT @prev := NULL, @rn := 0) AS vars
ORDER BY groupname, age DESC, person
) AS T1
WHERE rn <= 2
Véalo funcionando en línea: sqlfiddle
Editar Me acabo de dar cuenta de que bluefeet publicó una respuesta muy similar: +1 para él. Sin embargo, esta respuesta tiene dos pequeñas ventajas:
- Es una única consulta. Las variables se inicializan dentro de la instrucción SELECT.
- Maneja vínculos como se describe en la pregunta (orden alfabético por nombre).
Así que lo dejaré aquí por si a alguien le puede ayudar.
Prueba esto:
SELECT a.person, a.group, a.age FROM person AS a WHERE
(SELECT COUNT(*) FROM person AS b
WHERE b.group = a.group AND b.age >= a.age) <= 2
ORDER BY a.group ASC, a.age DESC
MANIFESTACIÓN