2011-04-16 17 views
13

Estoy tratando de seleccionar a cada usuario con su pago más reciente. La consulta que tengo ahora selecciona el primer pago de los usuarios. Es decir. si un usuario ha hecho dos pagos y los payment.id s son 10 y 11, la consulta selecciona el usuario con la información de ID de pago 10, no 11.Seleccione la fila más reciente con GROUP BY en MySQL

SELECT users.*, payments.method, payments.id AS payment_id 
    FROM `users` 
     LEFT JOIN `payments` ON users.id = payments.user_id 
GROUP BY users.id 

He añadido ORDER BY payments.id, pero la consulta parece ignóralo y aún selecciona el primer pago.

Toda ayuda apreciada. Gracias.

+0

posible duplicado de [puedo utilizar función de agregación (LAST) en mysql ??] (http://stackoverflow.com/questions/5495913/can-i-use-aggregation-function-last-in-mysql) –

+0

Consulte la respuesta en el enlace anterior para obtener el último registro del grupo –

+0

Incluso utilizando MAX (payments.id) esto no selecciona los valores correspondientes, es decir, método de pago. Si ejecuto la consulta con MAX (payments.id) selecciona la identificación de pago más reciente pero el primer método de pago. – Ryan

Respuesta

14

Usted quiere que el groupwise maximum; En esencia, el grupo de la tabla de pagos para identificar los registros máximos, a continuación, se unen el resultado de nuevo con ella misma a buscar las otras columnas:

SELECT users.*, payments.method, payments.id AS payment_id 
FROM payments NATURAL JOIN (
    SELECT user_id, MAX(id) AS id 
    FROM  payments 
    GROUP BY user_id 
) t RIGHT JOIN users ON users.id = t.user_id 

Tenga en cuenta que MAX(id) no puede ser el "pago más reciente", dependiendo de su aplicación y esquema: generalmente es mejor determinar "más reciente" basado en TIMESTAMP que basado en identificadores sintéticos como una columna de clave primaria AUTO_INCREMENT.

+0

+1 respuesta anterior pero el enlace fue muy útil gracias – user2844810

0

Teniendo esto un paso más allá, también podemos utilizar:

select payment_id, cust_id, amount, payment_method 
from my_table where payment_id in 
(
    select max(payment_id) from my_table group by cust_id 
); 

... pero esta consulta también está tomando demasiado tiempo en mi contexto. La selección interna es fumar rápido, pero la externa tarda un tiempo, y con solo 124 resultados desde el interior. Ideas?

0

Me he encontrado con esto antes. Agrupar por están más destinados a expresiones agregadas o registros idénticos. Mi investigación encontró que la mejor práctica es hacer algo como esto:

SELECT u.*, p.method, p.id AS payment_id 
    FROM (
     SELECT DISTINCT users.id 
     FROM users 
     ) ur 
    JOIN payments p 
    ON  p.id = 
     (
     SELECT pt.id 
     FROM payments pt 
     WHERE pt.user_id = ur.id 
     ORDER BY 
       pt.id DESC 
     LIMIT 1 
     ) 
+0

¿Podría explicar el razonamiento detrás de este enfoque, por favor? –

1

Mi solución:

SELECT 

u.codigo, 
u.nome, 
max(r.latitude), 
max(r.longitude), 
max(r.data_criacao) 

from TAB_REGISTRO_COORDENADAS r 

inner join TAB_USUARIO u 

on u.codigo = r.cd_usuario 

group by u.codigo 
2

He estado tratando con casi exactamente el mismo problema y encontré estas respuestas útiles. Mi prueba parece sugerir que puede hacer que sea un poco más sencilla que la respuesta aceptada, a saber .:

SELECT u.*, p.method, p.id AS payment_id 
FROM `users` u, `payments` p 
WHERE u.id = p.user_id 
    AND p.id = (SELECT MAX(p2.id) FROM payments p2 
        WHERE p2.user_id = u.id); 

que el rendimiento no hemos probado las diferencias, pero el PP el que estoy trabajando tiene más de 50.000 usuarios y más de 60.000 pagos y la consulta se ejecuta en 0.024 segundos.

0

leí la siguiente solución de hace mucho tiempo, pero no puedo encontrar el enlace al crédito, pero aquí va:

SELECT users.*, payments.method, payments.id AS payment_id, payments2.id 
FROM users 
JOIN payments 
    ON users.id = payments.user_id 
LEFT JOIN payments2 
    ON payments.user_id = payments2.user_id 
    AND payments.id < payments2.id 
WHERE payments2.id IS NULL 

Para entender cómo funciona esto, simplemente dejar caer el WHERE payments2.id IS NULL y se le ver lo que está sucediendo, por ejemplo, podría producir la siguiente salida (no he construido el esquema para probar esto, por lo que es pseudo-salida).Supongamos que hay los siguientes registros en payments:

id | user_id | method 
1 | 1  | VISA 
2 | 1  | VISA 
3 | 1  | VISA 
4 | 1  | VISA 

Y el SQL anterior (sin la cláusula WHERE payments2.id IS NULL) debe producir:

users.id | payments.method | payments.id | payments2.id 
1  | VISA   | 1   | 2 
1  | VISA   | 1   | 3 
1  | VISA   | 1   | 4 
1  | VISA   | 2   | 3 
1  | VISA   | 2   | 4 
1  | VISA   | 3   | 4 
1  | VISA   | 4   | NULL 

Como se puede ver el la última línea produce el resultado deseado, y ya que no hay payments2.id > 4, la combinación IZQUIERDA da como resultado un payments2.id = NULL.

He encontrado esta solución mucho más rápida (de mis primeras pruebas) que la respuesta aceptada.

Uso de un esquema diferente, pero una consulta similar, de 16095 registros:

select as1.*, as2.id 
from allocation_status as1 
left join allocation_status as2 
    on as1.allocation_id = as2.allocation_id 
    and as1.id < as2.id 
where as2.id is null; 

16095 rows affected, taking 4.1ms 

En comparación con la respuesta aceptada de MAX/subconsulta:

SELECT as1.* 
FROM allocation_status as1 
JOIN (
    SELECT max(id) as id 
    FROM allocation_status 
    group by allocation_id 
) as_max on as1.id = as_max.id 

16095 rows affected, taking 14.8ms