Select most recent row with GROUP BY in MySQL

后端 未结 6 2009
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-09 03:26

I\'m trying to select each user with their most recent payment. The query I have now selects the users first payment. I.e. if a user has made two payments and the paym

6条回答
  •  囚心锁ツ
    2020-12-09 03:39

    I read the following solution on SO long ago, but I can't find the link to credit, but here goes:

    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
    

    To understand how this works, just drop the WHERE payments2.id IS NULL and you'll see what is happening, for instance it could produce the following output (I haven't build the schema to test this, so it's pseudo-output). Assume there are the following records in payments:

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

    And the above SQL (without the WHERE payments2.id IS NULL clause) should produce:

    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
    

    As you can see the the last line produces the desired result, and since there's no payments2.id > 4, the LEFT JOIN results in a payments2.id = NULL.

    I've found this solution to be much faster (from my early tests) than the accepted answer.

    Using a different schema but a similar query, of 16095 records:

    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
    

    Compared to the accepted answer of MAX / subquery:

    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
    

提交回复
热议问题