Reputation: 459
My Objective is to grab the last few rows (I'm trying three) from a table for each account. I have no trouble getting the first rows but I'm having trouble making the top three most recent rows. For example, let's say I have the following table:
+--------+------------+------------+-----------+
| rownum | entryDate | particular | accountID |
+--------+------------+------------+-----------+
| 1 | 2015-10-01 | Item1 | 1 |
| 2 | 2015-10-01 | Item2 | 1 |
| 3 | 2015-10-02 | Item3 | 1 |
| 4 | 2015-10-02 | Item4 | 1 |
| 5 | 2015-10-02 | Item5 | 2 |
| 6 | 2015-10-03 | Item6 | 2 |
| 7 | 2015-10-05 | Item7 | 3 |
+--------+------------+------------+-----------+
What I'm trying to get is:
+--------+------------+------------+-----------+
| rownum | entryDate | particular | accountID |
+--------+------------+------------+-----------+
| 4 | 2015-10-02 | Item4 | 1 |
| 3 | 2015-10-02 | Item3 | 1 |
| 2 | 2015-10-01 | Item2 | 1 |
| 6 | 2015-10-03 | Item6 | 2 |
| 5 | 2015-10-02 | Item5 | 2 |
| 7 | 2015-10-05 | Item7 | 3 |
+--------+------------+------------+-----------+
Where Item1
was removed because it already had 3 before it.
I've tried the following code, but it doesn't retrieve the most recent
select rownum, entryDate, particular, accountID
from (
select entryDate, particular, accountID
@rownum := if(@account = accountID, @rownum + 1, 1) rownum,
@account := accountID
from entries
join ( select @rownum := 0, @account := 0 ) init
order by accountID, entryDate desc) t
where t.rownum <= 3 -- Limits the rows per account
If anyone could point me in the right direction that'd be great!
EDIT: However, what I'm retrieving as a result is:
+--------+------------+------------+-----------+
| rownum | entryDate | particular | accountID |
+--------+------------+------------+-----------+
| 3 | 2015-10-02 | Item3 | 1 |
| 2 | 2015-10-01 | Item2 | 1 |
| 1 | 2015-10-01 | Item1 | 1 |
| 6 | 2015-10-03 | Item6 | 2 |
| 5 | 2015-10-02 | Item5 | 2 |
| 7 | 2015-10-05 | Item7 | 3 |
+--------+------------+------------+-----------+
Also, if it helps, I've tested this on MySQL workbench, SQLbuddy, PHP (the web application), and PHPMyAdmin and they all produce the same result
Upvotes: 1
Views: 102
Reputation: 1269603
A safe way to write this query is:
select rownum, entryDate, particular, accountID
from (select entryDate, particular, accountID,
(@rownum := if(@account = accountID, @rownum + 1,
if(@account := accountID, 1, 1)
)
) as rownum,
from entries cross join
(select @rownum := 0, @account := 0 ) init
order by accountid, entryDate desc
) t
where t.rownum <= 3;
This is explained in the documentation:
As a general rule, other than in SET statements, you should never assign a value to a user variable and read the value within the same statement . . . In the following statement, you might think that MySQL will evaluate @a first and then do an assignment second:
SELECT @a, @a:=@a+1, ...;
However, the order of evaluation for expressions involving user variables is undefined.
Upvotes: 2
Reputation: 164699
Your query works for me if I fix the syntax error.
select rownum, entryDate, particular, accountID
from (
select entryDate, particular, accountID,
@rownum := if(@account = accountID, @rownum + 1, 1) rownum,
@account := accountID
from entries
join ( select @rownum := 0, @account := 0 ) init
ORDER BY accountid, entryDate desc
) t
where t.rownum <= 3;
rownum entryDate particular accountID
1 October, 02 2015 Item3 1
2 October, 02 2015 Item4 1
3 October, 01 2015 Item1 1
1 October, 03 2015 Item6 2
2 October, 02 2015 Item5 2
1 October, 05 2015 Item7 3
Note that Item1 and Item2 have the same entryDate. It's not predictable whether you'll get Item1, 3 and 4 or 2, 3 and 4. However, you should never get Item1, 2 and 3.
Also note that rownum
is not 1 through 7, but incremented separately for each account. This is correct. That your output differs for the same query. I think I know what happened.
Your very clever subquery relies on the order of operations of columns in the sub-select. In particular, this.
@rownum := if(@account = accountID, @rownum + 1, 1) rownum,
@account := accountID
That's the trick which assigns a separate set of rownums to each account. It relies on the fact that the rows are ordered by account and that @rownum
is being set before @account
.
Problem is, I don't think you can rely on that.
Upvotes: 1