在MySQL中选择GROUP BY的最新行

时间:2011-04-16 16:52:04

标签: mysql sql group-by

我正在尝试使用最近的付款选择每个用户。我现在的查询选择了用户的第一笔付款。即如果用户已进行了两次付款且payment.id为10和11,则查询会选择具有付款ID 10的信息的用户,而不是11。

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

我添加了ORDER BY payments.id,但查询似乎忽略了它,仍然会选择第一笔付款。

所有帮助表示赞赏。 感谢。

6 个答案:

答案 0 :(得分:19)

你想要groupwise maximum;本质上,将支付表分组以识别最大记录,然后将结果与自身连接起来以获取其他列:

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

请注意,MAX(id)可能不是“最近的付款”,具体取决于您的应用和架构:通常最好确定“最近的”基于TIMESTAMP而非基于合成标识符(例如AUTO_INCREMENT主键列。

答案 1 :(得分:2)

我刚刚处理了几乎完全相同的问题,并发现这些答案很有帮助。我的测试似乎暗示你可以使它比接受的答案稍微简单,即:

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);

我没有对性能进行性能测试,但我正在处理的数据库有超过50,000个用户和超过60,000个付款,查询运行时间为0.024秒。

答案 2 :(得分:2)

我很久以前在SO上阅读了以下解决方案,但我找不到信用的链接,但是这里有:

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

要理解这是如何工作的,只需删除WHERE payments2.id IS NULL,你就会看到发生了什么,例如它可以产生以下输出(我没有构建模式来测试它,所以它是伪的 - 输出)。假设payments中有以下记录:

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

上面的SQL(没有WHERE payments2.id IS NULL子句)应该产生:

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

正如您所看到的那样,最后一行产生了所需的结果,并且由于没有payments2.id > 4,因此LEFT JOIN会产生payments2.id = NULL

我发现这个解决方案比我接受的答案快得多(来自我的早期测试)。

使用不同的架构但类似的查询,16095条记录:

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

与接受的MAX /子查询答案相比:

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

答案 3 :(得分:1)

我的解决方案:

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

答案 4 :(得分:0)

更进一步,我们也可以使用:

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
);

...但是这个查询在我的上下文中也花了太长时间。内部选择吸烟快,但外部需要一段时间,内部只有124个结果。想法?

答案 5 :(得分:0)

我之前遇到过这种情况。分组依赖于聚合表达式或相同记录。我的研究发现最好这样做:

    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
        )