简体   繁体   中英

Mysql select top N scores per group FOR EACH User

I'm trying to help a buddy out with a "simple" website for his golf group. The database I'm trying to use will store all the players scores for every round completed. There are also two types of games that will be played and stored. Example below:

ID        Game Type     Score  
1             a           12
1             a           12
1             a           1
1             a           15
1             a           15
1             b           12
1             b           5
1             b           10
1             b           12
1             b           5
1             b           10
2             a           6
2             a           9
2             a           1
2             a           3 
2             a           2
2             b           8
2             b           10
2             b           15
2             b           3 
2             b           12

What I am trying to do is, Select the top 3 scores of Game type A for each user AND Select the top 2 scores from game type B for each user. What I need the select to yield so I can then sum the scores for each player:

ID        Game Type     Score  
1             a           12
1             a           15
1             a           15
1             b           12
1             b           12

2             a           6
2             a           9
2             a           3 
2             b           15
2             b           12

i found a similar question and solution from bonCodigo at Select highest 3 scores in each day for every user . I altered his sql to this:

-- for top 2 sum by user by each day
SELECT userid, sum(score), type
FROM scores t1
where 3 >=
(SELECT count(*) 
 from scores t2
 where t1.score <= t2.score
 and t1.userid = t2.userid
 and t1.type = t2.type
 order by t2.score desc)
group by userid, type 
;

-- for all two days top 2 sum by user
SELECT userid, sum(score)
FROM scores t1
where 3 >=
(SELECT count(*) 
 from scores t2
 where t1.score <= t2.score
 and t1.userid = t2.userid
 and t1.type = t2.type
 order by t2.score desc)
group by userid
;

The problem I am having is that if i want the top 3 scores, but the 3rd and 4th scores are equal, then it will not return the 3rd highest score; only the top two. Attached is the sqlfiddle i've been trying to use. Any help on how getting it to use top 3, no matter if they are all equal or not! Thanks!

my sqlfiddle

So, being unable to give up on this, i've come up with this monstrosity, using a stored procedure to get your desired output (essentially replicating what you'd do at the application level to achieve the same result).

create procedure leaderboard()
begin
  declare uid int;
  declare done int default 0;
    declare cur1 cursor for select distinct userid from score;
  declare continue handler for not found set done = 1;
  drop table if exists score_temp;
  create temporary table score_temp(userid int, score int, type int);
  open cur1;
  read_loop: LOOP
    fetch cur1 into uid;
    if done then
      close cur1;
      leave read_loop;
    end if;
    INNERBLOCK: begin
      declare done2 int default 0;       
      declare i_userid int;
      declare i_type int;
      declare i_score int;
      declare cur2 cursor for select * from score where userid = uid and type = 1 order by score desc limit 3;
      declare continue handler for not found set done2 = 1;
      open cur2;
      inner_loop: LOOP
        fetch cur2 into i_userid, i_score, i_type;
        if done2 then
          close cur2;
          leave inner_loop;
        end if;
        insert into score_temp values (i_userid, i_score, i_type);
      end loop inner_loop;
    end INNERBLOCK;
    INNERBLOCK: begin
      declare done2 int default 0;       
      declare i_userid int;
      declare i_type int;
      declare i_score int;
      declare cur2 cursor for select * from score where userid = uid and type = 2 order by score desc limit 2;
      declare continue handler for not found set done2 = 1;
      open cur2;
      inner_loop: LOOP
        fetch cur2 into i_userid, i_score, i_type;
        if done2 then
          close cur2;
          leave inner_loop;
        end if;
        insert into score_temp values (i_userid, i_score, i_type);
      end loop inner_loop;
    end INNERBLOCK;
  end loop read_loop;
  select * from score_temp;
end

then to get your leaderboard, your query is call leaderboard();

demo fiddle here: http://sqlfiddle.com/#!2/569fb2/1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM