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 payment.id
s are 10 and 11, the query selects the user with the info for payment id 10, not 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
I've added ORDER BY payments.id
, but the query seems to ignore it and still selects the first payment.
All help appreciated. Thanks.
You want the groupwise maximum; in essence, group the payments table to identify the maximal records, then join the result back with itself to fetch the other columns:
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
Note that MAX(id)
may not be the "most recent payment", depending on your application and schema: it's usually better to determine "most recent" based off TIMESTAMP
than based off synthetic identifiers such as an AUTO_INCREMENT
primary key column.
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
I've just been dealing with pretty much exactly the same problem and found these answers helpful. My testing seems to suggest you can make it slightly simpler than the accepted answer, viz.:
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);
I've not performance tested the differences but the db I'm working on has over 50,000 Users and over 60,000 payments and the query runs in 0.024 seconds.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With