2009-05-29 14 views
45

Tengo la siguiente tabla:Seleccionar los 3 registros más recientes en los que los valores de una columna son distintos

id  time  text  otheridentifier 
    ------------------------------------------- 
    1  6   apple  4 
    2  7   orange 4 
    3  8   banana 3 
    4  9   pear  3 
    5  10  grape  2 

Lo que quiero hacer es seleccionar los 3 registros más recientes (por desc tiempo), cuya otheridentifier s son distintos. Así pues, en este caso, el resultado sería id 's: 5, 4 y 2.

id = 3 se omitiría porque hay un registro más reciente con el mismo campo otheridentifier.

Esto es lo que he intentado hacer:

SELECT * FROM `table` GROUP BY (`otheridentifier`) ORDER BY `time` DESC LIMIT 3 

Sin embargo, me acaban de conseguir filas de id = 5, 3 , y en lugar de 5, 4, 2 como se esperaba.

¿Puede alguien decirme por qué esta consulta no me devolvió lo que esperaba? Traté de cambiar el ORDER BY a ASC pero esto simplemente reorganiza las filas devueltas a 1, 3, 5.

Respuesta

34

No devuelve lo que esperaba porque la agrupación ocurre antes de ordenar, como lo refleja la posición de las cláusulas en el Declaración de SQL. Desgraciadamente, tendrás que ser más elegante para obtener las filas que deseas. Prueba esto:

SELECT * 
FROM `table` 
WHERE `id` = (
    SELECT `id` 
    FROM `table` as `alt` 
    WHERE `alt`.`otheridentifier` = `table`.`otheridentifier` 
    ORDER BY `time` DESC 
    LIMIT 1 
) 
ORDER BY `time` DESC 
LIMIT 3 
+1

que recordaba el tiempo que pasé horas para fijar sQL como esto y resulta que MySQL 4.0 no admite consultas anidadas; p – Unreality

+1

@Unreality: Fortu nately la mayoría de las soluciones que implican subconsultas se pueden expresar como join si es necesario. :) – Rytmis

+1

Sí, pero los que tienen ORDER/LIMIT no se expresan fácilmente por JOIN ... –

2
SELECT * FROM table t1 
WHERE t1.time = 
    (SELECT MAX(time) FROM table t2 
    WHERE t2.otheridentifier = t1.otheridentifier) 
+0

¿Cómo seleccionaría esto la última fila por otroidentificador? – Andomar

+0

@Andomar: No debería intentar responder preguntas cuando no estoy completamente despierto. Cambié un poco los nombres de las columnas: vea si tiene más sentido ahora. :) – Rytmis

18

Usted puede unirse a la mesa sobre sí misma para filtrar la última entrada por otheridentifier, y luego tomar las 3 filas superiores de que:

SELECT last.* 
FROM `table` last 
LEFT JOIN `table` prev 
    ON prev.`otheridentifier` = last.`otheridentifier` 
    AND prev.`time` < last.`time` 
WHERE prev.`id` is null 
ORDER BY last.`time` DESC 
LIMIT 3 
+0

Andomar, ¿puedes explicar cómo MySQL está utilizando la tabla 'prev' en tu consulta? Intenté usar esto en una consulta local y recibo un error que dice 'database.prev' no existe. –

+0

Fiddle con los datos de OP y su consulta. No estoy seguro de por qué esta respuesta se votó 6 veces: no funciona aquí: http://sqlfiddle.com/#!2/ace0b/1 –

+1

@acoder: tiene razón, hubo un pequeño error en la consulta: el el nombre de la tabla después de 'left join' faltaba. He actualizado la respuesta. – Andomar

2

Andomar's answer es probablemente el mejor de que no usa una subconsulta.

Un enfoque alternativo:

select * 
from `table` t1 
where t1.`time` in (
        select max(s2.`time`) 
        from  `table` t2 
        group by t2.otheridentifier 
        ) 
+0

Creo que veo un problema aquí si los valores de tiempo no son únicos, esto podría devolver filas que no debería. Supongamos que hay un valor de tiempo que es el máximo para un identificador distinto, pero es, por ejemplo, el segundo más grande para otro identificador distinto. ¿Esta consulta no devolvería ambos otros identificadores? Aunque puedo estar completamente alejado, todavía estoy un poco cansado. :) – Rytmis

+0

@Rytmis: Sí, y también mi consulta, y la tuya :) jeje – Andomar

+0

@Andomar: Hmm, ¿estás seguro de mi consulta? Porque acabo de probarlo agregando una fila (6, 7, 'fresa', 3) - el valor de tiempo 7 es el más grande en el grupo que tiene otro identificador 4, pero el segundo más grande en el que tiene otro identificador 3. Mi query todavía solo devuelve las filas que el OP quería. ¿Mi caso de prueba está equivocado? :) – Rytmis

1

¿qué pasa con

SELECT *, max(time) FROM `table` group by otheridentifier 
+0

Editar - esto funciona con los datos del OP. Tuve algunas combinaciones adicionales en mi consulta. Esto funciona bien con los datos de OP. http://sqlfiddle.com/#!2/ace0b/2 –

+1

Parece que no funciona bien. En su sqlfiddle, debería mostrar naranja y pera, su valor de tiempo es mayor. – mahemoff

+0

@mahemoff Esto no funciona correctamente como dijiste. – GusDeCooL

4

tuve un requisito similar, pero no tenía criterios de selección más avanzados. El uso de algunos de las otras respuestas que no podía conseguir exactamente lo que necesitaba, pero me di cuenta que todavía puede hacer un GROUP BY y ORDER BY después de la siguiente manera:

SELECT t.* FROM (SELECT * FROM table ORDER BY time DESC) t 
GROUP BY t.otheridentifier 
+0

funciona bien para mí !! – wiLLiamcastrO

2

Se puede utilizar esta consulta para obtener la respuesta correcta:

SELECT * FROM 
     (SELECT * FROM `table` order by time DESC) 
      t group by otheridentifier 
0

esto también:

SELECT * FROM 
OrigTable T INNER JOIN 
( 
SELECT otheridentifier,max(time) AS duration 
FROM T 
GROUP BY otheridentifier) S 
ON S.duration = T.time AND S.otheridentifier = T.otheridentifier. 
Cuestiones relacionadas