2011-04-14 19 views
21

Daft SQL question. Tengo una tabla como tal ('PID' es autoincrementación col primaria)Consulta de MySQL, MAX() + GROUP BY

CREATE TABLE theTable (
    `pid` INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, 
    `timestamp` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 
    `cost` INT UNSIGNED NOT NULL, 
    `rid` INT NOT NULL, 
) Engine=InnoDB; 

datos de la tabla real:

INSERT INTO theTable (`pid`, `timestamp`, `cost`, `rid`) 
VALUES 
    (1, '2011-04-14 01:05:07', 1122, 1), 
    (2, '2011-04-14 00:05:07', 2233, 1), 
    (3, '2011-04-14 01:05:41', 4455, 2), 
    (4, '2011-04-14 01:01:11', 5566, 2), 
    (5, '2011-04-14 01:06:06', 345, 1), 
    (6, '2011-04-13 22:06:06', 543, 2), 
    (7, '2011-04-14 01:14:14', 5435, 3), 
    (8, '2011-04-14 01:10:13', 6767, 3) 
; 

Quiero obtener el PID de la última fila para cada deshacerse (1 resultado por RID único). Para los datos de ejemplo, me gustaría:

pid | MAX(timestamp)  | rid 
----------------------------------- 
5 | 2011-04-14 01:06:06 | 1 
3 | 2011-04-14 01:05:41 | 2 
7 | 2011-04-14 01:14:14 | 3 

He tratado de ejecutar la siguiente consulta:

SELECT MAX(timestamp),rid,pid FROM theTable GROUP BY rid 

y me sale:

max(timestamp)  ; rid; pid 
---------------------------- 
2011-04-14 01:06:06; 1 ; 1 
2011-04-14 01:05:41; 2 ; 3 
2011-04-14 01:14:14; 3 ; 7 

El PID devuelto es siempre la primera aparición de PID para un RID (row/pid 1 es la primera vez que se usa 1, row/pid 3 la primera vez que se utiliza RID 2, row/pid 7 es first time rid 3). Aunque devuelven la marca de tiempo máxima para cada librado, los pids no son los pids para las marcas de tiempo de la tabla original. ¿Qué consulta me daría los resultados que estoy buscando?

+0

Se puede mostrar el resultado de la consulta exacto que está ejecutando? –

+0

Se ha actualizado la pregunta :) – codinghands

+0

posible duplicado de [Obtener la fila que tiene el valor Máx. Para una columna] (http://stackoverflow.com/questions/121387/fetch-the-row-which-has-the-max- value-for-a-column) – outis

Respuesta

44

(Probado en PostgreSQL 9.something)

Identificar el RID y el sello de tiempo.

select rid, max(timestamp) as ts 
from test 
group by rid; 

1 2011-04-14 18:46:00 
2 2011-04-14 14:59:00 

Únete a ella.

select test.pid, test.cost, test.timestamp, test.rid 
from test 
inner join 
    (select rid, max(timestamp) as ts 
    from test 
    group by rid) maxt 
on (test.rid = maxt.rid and test.timestamp = maxt.ts) 
+0

Magia, trabajó una delicia. ¿Alguna idea de por qué 'SELECT MAX (timestamp), rid, pid FROM theTable GROUP BY rid' no funcionó? – codinghands

+2

Porque quiere *** para cada deshacer *** (el 'GROUP BY deshacer'), para mostrar" *** timestamp máximo *** "(el' MAX (timestamp) ') y para esa fila, con el máximo marca de tiempo, *** *** pid ***. Aquí es donde tu idea se atasca. Necesita una función de "ventana" para hacer eso, o para agrupar y luego 'UNIR 'a la subconsulta agrupada, como la solución de Catcall. MYSQL no tiene funciones de ventana. –

+2

Peor aún, MySQL no genera un error sino que obtiene un pid de una fila (aleatoria). –

-1

Probar:

select pid,cost, timestamp, rid from theTable order by timestamp DESC limit 2; 
+0

Esto solo devuelve las últimas 2 entradas. Debe ser la última fila para cada RID, 1 por RID. – codinghands

4
SELECT t.pid, t.cost, to.timestamp, t.rid 
FROM test as t 
JOIN (
    SELECT rid, max(tempstamp) AS maxtimestamp 
    FROM test GROUP BY rid 
) AS tmax 
    ON t.pid = tmax.pid and t.timestamp = tmax.maxtimestamp 
+0

Acaba de ser golpeado por @Catcall. :) ¡Gracias! – codinghands

0

También podría tener como subconsultas que:

SELECT (SELECT MIN(t2.pid) 
     FROM test t2 
     WHERE t2.rid = t.rid 
      AND t2.timestamp = maxtimestamp 
     ) AS pid 
    , MAX(t.timestamp) AS maxtimestamp 
    , t.rid 
FROM test t 
GROUP BY t.rid 

Pero de esta manera, tendrá una más sub consulta si desea cost incluidos en las columnas que se muestran, etc.

Por lo tanto, el group by y join es la mejor solución.

2

Creé un índice sobre deshacer y marca de tiempo.

SELECT test.pid, test.cost, test.timestamp, test.rid 
FROM theTable AS test 
LEFT JOIN theTable maxt 
ON maxt.rid = test.rid 
AND maxt.timestamp > test.timestamp 
WHERE maxt.rid IS NULL 

Mostrando filas a 0 - 2 (3 en total, Consulta tomó 0,0104 seg)

Este método seleccionará todos los valores deseados de theTable (test), a la izquierda de unirse a sí mismo (MaxI) en todas las marcas de tiempo mayor que el de la prueba con la misma libra. Cuando la marca de tiempo ya es la más alta en la prueba, no hay coincidencias en maxt, que es lo que estamos buscando, los valores en maxt se convierten en NULL. Ahora usamos la cláusula WHERE maxt.rid IS NULL o cualquier otra columna en maxt.

6
select * 
from (
    select `pid`, `timestamp`, `cost`, `rid` 
    from theTable 
    order by `timestamp` desc 
) as mynewtable 
group by mynewtable.`rid` 
order by mynewtable.`timestamp` 

Hope I helps!

+0

Solución clara, corta y simple. –

+0

... y el único que realmente funcionó para mí. – Debriter

+0

Esto es brillante, tan simple. También funcionan otras respuestas, pero prefiero evitar unirme a una tabla en sí misma. – ProgrammingWithRandy

0

Si se quiere evitar un JOIN, puede utilizar:

SELECT pid, rid FROM theTable t1 WHERE t1.pid IN (SELECT MAX(t2.pid) FROM theTable t2 GROUP BY t2.rid); 
+0

Según los datos, el PID superior no está necesariamente asociado con la marca de tiempo posterior. –

+0

Tienes razón. Solo funciona si timestamp no se modifica en ninguna parte del programa y solo almacena de creation_date. Si no, como pid es autoinc y timestamp current_timestamp I asume higher PID do corresponde a la marca de tiempo posterior –