user1753622
user1753622

Reputation: 288

MySQL limit records for a group

I've read the examples, but cannot get it to work for my example.

I have a table 'player' with columns 'pid' and 'name' and a table 'card' with columns 'pid', 'points'.

There are multiple card records to a player record.

How can I query the top 5 records per player?

This query returns eveything I need, but doesn't limit to 5 records per name.

SELECT p.pid, p.name, c.points as lp
FROM player as p, card as c
WHERE p.pid=c.pid
ORDER BY p.name,  lp DESC

Upvotes: 1

Views: 109

Answers (2)

spencer7593
spencer7593

Reputation: 108380

MySQL does not support the "analytic" type function necessary to return the specified resultset. We can emulate the function by making use of MySQL "user variables".

SELECT IF(@prev_name = p.name,@n:=@n+1,@n:=1) AS seq
     , @prev_name := p.name AS name
     , c.points as lp
  FROM player as p
  JOIN card as c
    ON c.pid = p.pid
  JOIN (SELECT @n := 0, @prev_name := NULL) i
 HAVING seq <= 5
 ORDER BY p.name, lp DESC

Note that the resultset from this statement differs from your original statement, in that it returns an extra column "seq". This column will return values of 1,2,3,etc. indicating whether this was the "first", "second", "third", etc. row for a particular p.name.

The expression used to derive this is basically checking whether the value of p.name on the current row matches the value from the previous row. If it matches, we add a 1; if it doesn't match, then we reset to zero.

The query will actually generate a result set for all rows for each p.name; the HAVING clause will restrict the rows that are actually returned to the client.

Upvotes: 1

Taryn
Taryn

Reputation: 247670

You can use variables to assign a rank to each user:

select pid, name, lp
from
(
    SELECT pid, 
        name, 
        points as lp,
        @prev := @curr,
        @curr := name,
        @rank := IF(@prev = @curr, @rank+1, 1) AS rank
    FROM
    (
      select p.pid, name, points
      FROM player as p
      INNER JOIN card as c
          ON p.pid=c.pid
    ) src, (SELECT @curr := null, @prev := null, @rank := 1) r
    ORDER BY name, points desc
) src
where rank <= 5
order by name, lp DESC;

See SQL Fiddle with Demo

Upvotes: 2

Related Questions