una ultimo traer tabla seleccionar registro por obtener mostrar mayor insertado grupo fecha cada actualizar sql mysql group-by greatest-n-per-group

ultimo - sql seleccionar registro mayor fecha



Recuperando el Ășltimo registro en cada grupo-MySQL (21)

¿Hay alguna manera de que podamos usar este método para eliminar duplicados en una tabla? El conjunto de resultados es básicamente una colección de registros únicos, por lo que si pudiéramos eliminar todos los registros que no están en el conjunto de resultados, ¿no tendríamos duplicados? Intenté esto pero mySQL dio un error 1093.

DELETE FROM messages WHERE id NOT IN (SELECT m1.id FROM messages m1 LEFT JOIN messages m2 ON (m1.name = m2.name AND m1.id < m2.id) WHERE m2.id IS NULL)

¿Hay alguna manera de guardar la salida en una variable temporal y luego eliminarla de NOT IN (variable temporal)? @Bill gracias por una solución muy útil.

EDITAR: Creo que encontré la solución:

DROP TABLE IF EXISTS UniqueIDs; CREATE Temporary table UniqueIDs (id Int(11)); INSERT INTO UniqueIDs (SELECT T1.ID FROM Table T1 LEFT JOIN Table T2 ON (T1.Field1 = T2.Field1 AND T1.Field2 = T2.Field2 #Comparison Fields AND T1.ID < T2.ID) WHERE T2.ID IS NULL); DELETE FROM Table WHERE id NOT IN (SELECT ID FROM UniqueIDs);

Hay una tabla de messages que contiene datos como se muestra a continuación:

Id Name Other_Columns ------------------------- 1 A A_data_1 2 A A_data_2 3 A A_data_3 4 B B_data_1 5 B B_data_2 6 C C_data_1

Si ejecuto una consulta, select * from messages group by name , obtendré el resultado como:

1 A A_data_1 4 B B_data_1 6 C C_data_1

¿Qué consulta devolverá el siguiente resultado?

3 A A_data_3 5 B B_data_2 6 C C_data_1

Es decir, se debe devolver el último registro de cada grupo.

En la actualidad, esta es la consulta que uso:

SELECT * FROM (SELECT * FROM messages ORDER BY id DESC) AS x GROUP BY name

Pero esto parece altamente ineficiente. ¿Alguna otra forma de lograr el mismo resultado?


Aquí está mi solución:

SELECT DISTINCT NAME, MAX(MESSAGES) OVER(PARTITION BY NAME) MESSAGES FROM MESSAGE;


Aquí hay dos sugerencias. Primero, si mysql admite ROW_NUMBER (), es muy simple:

WITH Ranked AS ( SELECT Id, Name, OtherColumns, ROW_NUMBER() OVER ( PARTITION BY Name ORDER BY Id DESC ) AS rk FROM messages ) SELECT Id, Name, OtherColumns FROM messages WHERE rk = 1;

Supongo que por "último" quiere decir último en el orden de identificación. Si no, cambie la cláusula ORDER BY de la ventana ROW_NUMBER () en consecuencia. Si ROW_NUMBER () no está disponible, esta es otra solución:

En segundo lugar, si no lo hace, a menudo es una buena manera de proceder:

SELECT Id, Name, OtherColumns FROM messages WHERE NOT EXISTS ( SELECT * FROM messages as M2 WHERE M2.Name = messages.Name AND M2.Id > messages.Id )

En otras palabras, seleccione los mensajes donde no haya un mensaje de identificación posterior con el mismo nombre.


Claramente hay muchas formas diferentes de obtener los mismos resultados, su pregunta parece ser cuál es una forma eficiente de obtener los últimos resultados en cada grupo en MySQL. Si está trabajando con grandes cantidades de datos y suponiendo que está utilizando InnoDB incluso con las últimas versiones de MySQL (como 5.7.21 y 8.0.4-rc), es posible que no haya una forma eficiente de hacerlo.

A veces necesitamos hacer esto con tablas con más de 60 millones de filas.

Para estos ejemplos, usaré datos con solo alrededor de 1.5 millones de filas donde las consultas necesitarían encontrar resultados para todos los grupos en los datos. En nuestros casos reales, a menudo tendríamos que devolver datos de unos 2.000 grupos (lo que hipotéticamente no requeriría examinar gran parte de los datos).

Usaré las siguientes tablas:

CREATE TABLE temperature( id INT UNSIGNED NOT NULL AUTO_INCREMENT, groupID INT UNSIGNED NOT NULL, recordedTimestamp TIMESTAMP NOT NULL, recordedValue INT NOT NULL, INDEX groupIndex(groupID, recordedTimestamp), PRIMARY KEY (id) ); CREATE TEMPORARY TABLE selected_group(id INT UNSIGNED NOT NULL, PRIMARY KEY(id));

La tabla de temperatura se rellena con aproximadamente 1,5 millones de registros aleatorios y con 100 grupos diferentes. El grupo seleccionado se llena con esos 100 grupos (en nuestros casos, esto normalmente sería menos del 20% para todos los grupos).

Como estos datos son aleatorios, significa que varias filas pueden tener las mismas marcas de tiempo registradas. Lo que queremos es obtener una lista de todos los grupos seleccionados en orden de groupID con la última marca de tiempo registrada para cada grupo, y si el mismo grupo tiene más de una fila coincidente como esa, entonces la última identificación coincidente de esas filas.

Si hipotéticamente MySQL tenía una función last () que devolvía valores de la última fila en una cláusula ORDER BY especial, entonces simplemente podríamos hacer:

SELECT last(t1.id) AS id, t1.groupID, last(t1.recordedTimestamp) AS recordedTimestamp, last(t1.recordedValue) AS recordedValue FROM selected_group g INNER JOIN temperature t1 ON t1.groupID = g.id ORDER BY t1.recordedTimestamp, t1.id GROUP BY t1.groupID;

que solo necesitaría examinar unas 100 filas en este caso, ya que no usa ninguna de las funciones normales de GROUP BY. Esto se ejecutaría en 0 segundos y por lo tanto sería altamente eficiente. Tenga en cuenta que normalmente en MySQL veríamos una cláusula ORDER BY después de la cláusula GROUP BY; sin embargo, esta cláusula ORDER BY se usa para determinar la ORDER para la función last (), si estuviera después de GROUP BY, entonces estaría ordenando los GRUPOS. Si no existe una cláusula GROUP BY, los últimos valores serán los mismos en todas las filas devueltas.

Sin embargo, MySQL no tiene esto, así que echemos un vistazo a las diferentes ideas de lo que tiene y probemos que ninguna de ellas es eficiente.

Ejemplo 1

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue FROM selected_group g INNER JOIN temperature t1 ON t1.id = ( SELECT t2.id FROM temperature t2 WHERE t2.groupID = g.id ORDER BY t2.recordedTimestamp DESC, t2.id DESC LIMIT 1 );

Este examinó 3,009,254 filas y tomó ~ 0,859 segundos en 5.7.21 y ligeramente más largo en 8.0.4-rc.

Ejemplo 2

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue FROM temperature t1 INNER JOIN ( SELECT max(t2.id) AS id FROM temperature t2 INNER JOIN ( SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestamp FROM selected_group g INNER JOIN temperature t3 ON t3.groupID = g.id GROUP BY t3.groupID ) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestamp GROUP BY t2.groupID ) t5 ON t5.id = t1.id;

Esto examinó 1,505,331 filas y tomó ~ 1.25 segundos en 5.7.21 y ligeramente más largo en 8.0.4-rc.

Ejemplo 3

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue FROM temperature t1 WHERE t1.id IN ( SELECT max(t2.id) AS id FROM temperature t2 INNER JOIN ( SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestamp FROM selected_group g INNER JOIN temperature t3 ON t3.groupID = g.id GROUP BY t3.groupID ) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestamp GROUP BY t2.groupID ) ORDER BY t1.groupID;

Esto examinó 3,009,685 filas y tomó ~ 1.95 segundos en 5.7.21 y un poco más en 8.0.4-rc

Ejemplo 4

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue FROM selected_group g INNER JOIN temperature t1 ON t1.id = ( SELECT max(t2.id) FROM temperature t2 WHERE t2.groupID = g.id AND t2.recordedTimestamp = ( SELECT max(t3.recordedTimestamp) FROM temperature t3 WHERE t3.groupID = g.id ) );

Esto examinó 6,137,810 filas y tomó ~ 2.2 segundos en 5.7.21 y ligeramente más largo en 8.0.4-rc

Ejemplo 5

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue FROM ( SELECT t2.id, t2.groupID, t2.recordedTimestamp, t2.recordedValue, row_number() OVER ( PARTITION BY t2.groupID ORDER BY t2.recordedTimestamp DESC, t2.id DESC ) AS rowNumber FROM selected_group g INNER JOIN temperature t2 ON t2.groupID = g.id ) t1 WHERE t1.rowNumber = 1;

Esto examinó 6,017,808 filas y tomó ~ 4.2 segundos en 8.0.4-rc

Ejemplo 6

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue FROM ( SELECT last_value(t2.id) OVER w AS id, t2.groupID, last_value(t2.recordedTimestamp) OVER w AS recordedTimestamp, last_value(t2.recordedValue) OVER w AS recordedValue FROM selected_group g INNER JOIN temperature t2 ON t2.groupID = g.id WINDOW w AS ( PARTITION BY t2.groupID ORDER BY t2.recordedTimestamp, t2.id RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) ) t1 GROUP BY t1.groupID;

Esto examinó 6,017,908 filas y tomó ~ 17.5 segundos en 8.0.4-rc

Ejemplo 7

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue FROM selected_group g INNER JOIN temperature t1 ON t1.groupID = g.id LEFT JOIN temperature t2 ON t2.groupID = g.id AND ( t2.recordedTimestamp > t1.recordedTimestamp OR (t2.recordedTimestamp = t1.recordedTimestamp AND t2.id > t1.id) ) WHERE t2.id IS NULL ORDER BY t1.groupID;

Este estaba tomando para siempre así que tuve que matarlo.


Esta es otra forma de obtener el último registro relacionado utilizando GROUP_CONCAT con orden by y SUBSTRING_INDEX para seleccionar uno de los registros de la lista

SELECT `Id`, `Name`, SUBSTRING_INDEX( GROUP_CONCAT( `Other_Columns` ORDER BY `Id` DESC SEPARATOR ''||'' ), ''||'', 1 ) Other_Columns FROM messages GROUP BY `Name`

La consulta anterior Other_Columns todas las Other_Columns que están en el mismo grupo de Name y utilizando ORDER BY id DESC unirá todas las Other_Columns en un grupo específico en orden descendente con el separador provisto en mi caso que he usado || , al usar SUBSTRING_INDEX sobre esta lista se seleccionará el primero

Demostración de violín


Hola, @Vijay Dev si los mensajes de tu tabla contienen Id, que es una clave primaria de incremento automático, para obtener el último registro de la clave principal que tu consulta debe leer como se muestra a continuación:

SELECT m1.* FROM messages m1 INNER JOIN (SELECT max(Id) as lastmsgId FROM messages GROUP BY Name) m2 ON m1.Id=m2.lastmsgId


La siguiente consulta funcionará bien según su pregunta.

SELECT M1.* FROM MESSAGES M1, ( SELECT SUBSTR(Others_data,1,2),MAX(Others_data) AS Max_Others_data FROM MESSAGES GROUP BY 1 ) M2 WHERE M1.Others_data = M2.Max_Others_data ORDER BY Others_data;


Llegué a una solución diferente, que es obtener los ID para la última publicación dentro de cada grupo, luego seleccionar de la tabla de mensajes usando el resultado de la primera consulta como el argumento para una construcción WHERE x IN :

SELECT id, name, other_columns FROM messages WHERE id IN ( SELECT MAX(id) FROM messages GROUP BY name );

No sé cómo funciona esto en comparación con algunas de las otras soluciones, pero funcionó espectacularmente para mi mesa con más de 3 millones de filas. (4 segundos de ejecución con más de 1200 resultados)

Esto debería funcionar tanto en MySQL como en SQL Server.


MySQL 8.0 ahora admite funciones de ventanas, como casi todas las implementaciones populares de SQL. Con esta sintaxis estándar, podemos escribir las mejores consultas por grupo:

WITH ranked_messages AS ( SELECT m.*, ROW_NUMBER() OVER (PARTITION BY name ORDER BY id DESC) AS rn FROM messages AS m ) SELECT * FROM ranked_messages WHERE rn = 1;

A continuación se muestra la respuesta original que escribí para esta pregunta en 2009:

Escribo la solución de esta manera:

SELECT m1.* FROM messages m1 LEFT JOIN messages m2 ON (m1.name = m2.name AND m1.id < m2.id) WHERE m2.id IS NULL;

En cuanto al rendimiento, una solución u otra puede ser mejor, dependiendo de la naturaleza de sus datos. Por lo tanto, debe probar ambas consultas y usar una que sea mejor en su rendimiento dada su base de datos.

Por ejemplo, tengo una copia del volcado de datos de agosto de . Voy a usar eso para la evaluación comparativa. Hay 1,114,357 filas en la tabla de Posts . Esto se ejecuta en MySQL 5.0.75 en mi Macbook Pro 2.40GHz.

Escribiré una consulta para encontrar la publicación más reciente para un ID de usuario determinado (el mío).

Primero, utilizando la técnica shown por @Eric con GROUP BY en una subconsulta:

SELECT p1.postid FROM Posts p1 INNER JOIN (SELECT pi.owneruserid, MAX(pi.postid) AS maxpostid FROM Posts pi GROUP BY pi.owneruserid) p2 ON (p1.postid = p2.maxpostid) WHERE p1.owneruserid = 20860; 1 row in set (1 min 17.89 sec)

Incluso el análisis EXPLAIN lleva más de 16 segundos:

+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+ | 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 76756 | | | 1 | PRIMARY | p1 | eq_ref | PRIMARY,PostId,OwnerUserId | PRIMARY | 8 | p2.maxpostid | 1 | Using where | | 2 | DERIVED | pi | index | NULL | OwnerUserId | 8 | NULL | 1151268 | Using index | +----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+ 3 rows in set (16.09 sec)

Ahora produzca el mismo resultado de la consulta usando mi técnica con LEFT JOIN :

SELECT p1.postid FROM Posts p1 LEFT JOIN posts p2 ON (p1.owneruserid = p2.owneruserid AND p1.postid < p2.postid) WHERE p2.postid IS NULL AND p1.owneruserid = 20860; 1 row in set (0.28 sec)

El análisis de EXPLAIN muestra que ambas tablas pueden usar sus índices:

+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+ | 1 | SIMPLE | p1 | ref | OwnerUserId | OwnerUserId | 8 | const | 1384 | Using index | | 1 | SIMPLE | p2 | ref | PRIMARY,PostId,OwnerUserId | OwnerUserId | 8 | const | 1384 | Using where; Using index; Not exists | +----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+ 2 rows in set (0.00 sec)

Aquí está el DDL para mi tabla de Posts :

CREATE TABLE `posts` ( `PostId` bigint(20) unsigned NOT NULL auto_increment, `PostTypeId` bigint(20) unsigned NOT NULL, `AcceptedAnswerId` bigint(20) unsigned default NULL, `ParentId` bigint(20) unsigned default NULL, `CreationDate` datetime NOT NULL, `Score` int(11) NOT NULL default ''0'', `ViewCount` int(11) NOT NULL default ''0'', `Body` text NOT NULL, `OwnerUserId` bigint(20) unsigned NOT NULL, `OwnerDisplayName` varchar(40) default NULL, `LastEditorUserId` bigint(20) unsigned default NULL, `LastEditDate` datetime default NULL, `LastActivityDate` datetime default NULL, `Title` varchar(250) NOT NULL default '''', `Tags` varchar(150) NOT NULL default '''', `AnswerCount` int(11) NOT NULL default ''0'', `CommentCount` int(11) NOT NULL default ''0'', `FavoriteCount` int(11) NOT NULL default ''0'', `ClosedDate` datetime default NULL, PRIMARY KEY (`PostId`), UNIQUE KEY `PostId` (`PostId`), KEY `PostTypeId` (`PostTypeId`), KEY `AcceptedAnswerId` (`AcceptedAnswerId`), KEY `OwnerUserId` (`OwnerUserId`), KEY `LastEditorUserId` (`LastEditorUserId`), KEY `ParentId` (`ParentId`), CONSTRAINT `posts_ibfk_1` FOREIGN KEY (`PostTypeId`) REFERENCES `posttypes` (`PostTypeId`) ) ENGINE=InnoDB;


Prueba esto:

SELECT jos_categories.title AS name, joined .catid, joined .title, joined .introtext FROM jos_categories INNER JOIN (SELECT * FROM (SELECT `title`, catid, `created`, introtext FROM `jos_content` WHERE `sectionid` = 6 ORDER BY `id` DESC) AS yes GROUP BY `yes`.`catid` DESC ORDER BY `yes`.`created` DESC) AS joined ON( joined.catid = jos_categories.id )


Puedes verlo desde aquí también.

http://sqlfiddle.com/#!9/ef42b/9

Primera solucion

SELECT d1.ID,Name,City FROM Demo_User d1 INNER JOIN (SELECT MAX(ID) AS ID FROM Demo_User GROUP By NAME) AS P ON (d1.ID=P.ID);

Segunda solucion

SELECT * FROM (SELECT * FROM Demo_User ORDER BY ID DESC) AS T GROUP BY NAME ;


Qué tal esto:

SELECT DISTINCT ON (name) * FROM messages ORDER BY name, id DESC;

Tuve un problema similar (en hard postgresql) y en una tabla de registros de 1M. Esta solución toma 1.7s contra 44s producidos por el que tiene LEFT JOIN. En mi caso, tuve que filtrar el corrispondant de su campo de nombre contra valores NULL, lo que resultó en mejores resultados en 0.2 segundos


Si desea la última fila para cada Name , puede asignar un número de fila a cada grupo de filas por Name y orden por Id en orden descendente.

CONSULTA

SELECT t1.Id, t1.Name, t1.Other_Columns FROM ( SELECT Id, Name, Other_Columns, ( CASE Name WHEN @curA THEN @curRow := @curRow + 1 ELSE @curRow := 1 AND @curA := Name END ) + 1 AS rn FROM messages t, (SELECT @curRow := 0, @curA := '''') r ORDER BY Name,Id DESC )t1 WHERE t1.rn = 1 ORDER BY t1.Id;

SQL Fiddle


Si el rendimiento es realmente su preocupación, puede introducir una nueva columna en la tabla llamada IsLastInGroup de tipo BIT.

Establézcalo en verdadero en las últimas columnas y manténgalo con cada fila insertar / actualizar / eliminar. Las escrituras serán más lentas, pero te beneficiarás con las lecturas. Depende de su caso de uso y lo recomiendo solo si está centrado en la lectura.

Entonces tu consulta se verá como:

SELECT * FROM Messages WHERE IsLastInGroup = 1


Solución por sub consulta

select * from messages where id in (select max(id) from messages group by Name)

Solución por unión condición fiddle link

select m1.* from messages m1 left outer join messages m2 on ( m1.id<m2.id and m1.name=m2.name ) where m2.id is null

La razón de esta publicación es dar solo el enlace de violín. El mismo SQL ya se proporciona en otras respuestas.


Todavía no he probado con grandes bases de datos, pero creo que esto podría ser más rápido que unir tablas:

SELECT *, Max(Id) FROM messages GROUP BY Name


Un acercamiento con velocidad considerable es el siguiente.

SELECT * FROM messages a WHERE Id = (SELECT MAX(Id) FROM messages WHERE a.Name = Name)

Resultado

Id Name Other_Columns 3 A A_data_3 5 B B_data_2 6 C C_data_1


Usa tu subquery para devolver la agrupación correcta, porque estás a mitad de camino.

Prueba esto:

select a.* from messages a inner join (select name, max(id) as maxid from messages group by name) as b on a.id = b.maxid

Si no es id , desea el máximo de:

select a.* from messages a inner join (select name, max(other_col) as other_col from messages group by name) as b on a.name = b.name and a.other_col = b.other_col

De esta manera, evita subconsultas y / o ordenaciones correlacionadas en sus subconsultas, que tienden a ser muy lentas / ineficientes.


UPD: 2017-03-31, la versión 5.7.5 de MySQL hizo que el conmutador ONLY_FULL_GROUP_BY se habilitara por defecto (por lo tanto, las consultas GROUP BY no deterministas se deshabilitaron). Además, actualizaron la implementación de GROUP BY y la solución podría dejar de funcionar como se esperaba, incluso con el interruptor deshabilitado. Uno tiene que comprobar.

La solución anterior de Bill Karwin funciona bien cuando el recuento de elementos dentro de los grupos es bastante pequeño, pero el rendimiento de la consulta se vuelve malo cuando los grupos son bastante grandes, ya que la solución requiere aproximadamente n*n/2 + n/2 de solo comparaciones IS NULL .

Hice mis pruebas en una tabla InnoDB de 18684446 filas con 1182 grupos. La tabla contiene los resultados de las pruebas para las pruebas funcionales y tiene la clave principal (test_id, request_id) . Por lo tanto, test_id es un grupo y estaba buscando el último request_id para cada test_id .

La solución de Bill ya se ha estado ejecutando durante varias horas en mi dell e4310 y no sé cuándo finalizará, aunque funcione con un índice de cobertura (por lo tanto, using index en EXPLAIN).

Tengo un par de otras soluciones basadas en las mismas ideas:

  • si el índice subyacente es el índice BTREE (que suele ser el caso), el par más grande (group_id, item_value) es el último valor dentro de cada group_id , que es el primero para cada group_id si recorremos el índice en orden descendente;
  • si leemos los valores que están cubiertos por un índice, los valores se leen en el orden del índice;
  • cada índice contiene implícitamente columnas de clave primaria adjuntas a eso (es decir, la clave principal se encuentra en el índice de cobertura). En las soluciones a continuación, opero directamente en la clave principal, en su caso, solo tendrá que agregar columnas de clave principal en el resultado.
  • en muchos casos es mucho más barato recopilar los ID de fila requeridos en el orden requerido en una subconsulta y unir el resultado de la subconsulta en el ID. Como para cada fila en el resultado de la subconsulta, MySQL necesitará una única recuperación basada en la clave primaria, la subconsulta se colocará primero en la unión y las filas se mostrarán en el orden de los identificadores de la subconsulta (si omitimos explícitamente ORDER BY para la unión)

3 formas en que MySQL usa índices es un gran artículo para entender algunos detalles.

Solución 1

Este es increíblemente rápido, toma aproximadamente 0,8 segundos en mis 18M + filas:

SELECT test_id, MAX(request_id), request_id FROM testresults GROUP BY test_id DESC;

Si desea cambiar el orden a ASC, póngalo en una subconsulta, devuelva solo los identificadores y utilícelo como subconsulta para unirse al resto de las columnas:

SELECT test_id, request_id FROM ( SELECT test_id, MAX(request_id), request_id FROM testresults GROUP BY test_id DESC) as ids ORDER BY test_id;

Esta toma alrededor de 1,2 segundos en mis datos.

Solucion 2

Aquí hay otra solución que toma alrededor de 19 segundos para mi mesa:

SELECT test_id, request_id FROM testresults, (SELECT @group:=NULL) as init WHERE IF(IFNULL(@group, -1)=@group:=test_id, 0, 1) ORDER BY test_id DESC, request_id DESC

También devuelve las pruebas en orden descendente. Es mucho más lento ya que realiza una exploración de índice completa, pero está aquí para darle una idea de cómo generar N filas máximas para cada grupo.

La desventaja de la consulta es que su caché de consulta no puede almacenar en caché su resultado.


SELECT column1, column2 FROM table_name WHERE id IN (SELECT MAX(id) FROM table_name GROUP BY column1) ORDER BY column1 ;


select * from messages group by name desc