2

Lets say I have the following table:

id  coulmn_id  value    date
1      10      'a'     2016-04-01
1      11      'b'     2015-10-02
1      12      'a'     2016-07-03
1      13      'a'     2015-11-11
2      11      'c'     2016-01-10
2      23      'd'     2016-01-11
3      11      'c'     2016-01-09
3      111     'd'     2016-01-11
3      222      'c'     2016-01-10
3      333      'd'     2016-01-11

for n = 3, I want to get the latest n records<=3 for each id. So I will have the following output:

id  column_id  value    date
1      10        'a'     2016-04-01
1      12        'a'     2016-07-03
1      13        'a'     2015-11-11
2      11        'c'     2016-01-10
2      23        'd'     2016-01-11
3      111       'd'     2016-01-11
3      222       'c'     2016-01-10
3      333       'd'     2016-01-11
futurenext110
  • 1,991
  • 6
  • 26
  • 31

2 Answers2

5

I am answering because the referenced question has an unstable answer (I'll comment on that there).

Here is a solution that should work:

select t.*
from (select t.*,
             (@rn := if(@id = id, @rn + 1,
                        if(@id := id, 1, 1)
                       )
             ) as seqnum
      from t cross join
           (select @rn := 0, @id := -1) params
      order by id, date desc
     ) t
where seqnum <= 3;

The difference in the solutions is that the variable assignments are all in a single expression. MySQL does not guarantee the order of evaluation of expressions, so this is very important if the code is going to work consistently.

Gordon Linoff
  • 1,242,037
  • 58
  • 646
  • 786
  • I don't mean to hassle you across multiple answers of this type, this is more something the question poster will need to sort out, but technically this answer is potentially unstable if the `date` field is not unique. _Just adding to a good answer._ – Uueerdo Jul 22 '16 at 22:02
  • @Uueerdo . . . That is a reasonable observation. However, the answer is correct unless the *data* has an issue. The referenced answer can be incorrect from the perspective of the *database*. – Gordon Linoff Jul 23 '16 at 02:26
0

You could do this with the use of variables. First go through the results in reverse order and assign a row number, then filter the results for row numbers less or equal to 3, and re-order:

select   id, value, date
from     (
           select      id, value, date,
                       @rn := if(@id = id, @rn+1, if (@id := id, 1, 1)) rn
           from        mytable,
           cross join  (@id := null, @rn := null) init
           order by    id, date desc
         ) as base
where    rn <= 3
order by id, date asc
trincot
  • 317,000
  • 35
  • 244
  • 286