mysql rank from results - mysql

Sorry for posting another question about mysql ranking but all questions and answers which I already looked didn't help me....
I have mysql table of user points. User can have more results. My goal is to get max result from user and its rank.
CREATE TABLE results
(`user_id` int, `points` int);
INSERT INTO results VALUES
(1,10),
(2,20),
(3,20),
(4,30),
(4,60),
(5,5),
(1,80);
So upper solution would be:
rank | user_id | points
1 1 80
2 4 60
3 3 20
3 2 20
4 5 5

The following query does the trick:
SET #rank=0;
SET #points=0;
SELECT #rank := IF(#points = a.points, #rank, #rank + 1) AS rank, a.user_id, #points := a.points AS points
FROM (
SELECT user_id, MAX(points) as points
FROM results
GROUP BY user_id
) a
ORDER BY a.points DESC;
I have also created an SQLFiddle of it so you can see that it works: http://sqlfiddle.com/#!2/7ba2f/12

Use a user defined variable to produce the rank when selecting from an aggregated aliased query that calculates the maximum for each user:
select
(#rank := ifnull(#rank, 0) + 1) as rank,
user_id,
points
from (select
user_id,
max(points) as points
from results
group by 1
order by 2 desc) x
FYI, a UDV starts out life as null, hence the ifnull() call.

Related

Get the record details and calculate the ranking

I have a table of exam results. I need to get the record of a specific participant and get his/her ranking too.
for example, the participant with the participant_id 15 must have the ranking 3 amongst the total 4 records. so the result i am looking for would be:
id: 1
exam_id: 3
participant_id: 15
score: 343.23
ranking: 3
I know I can get the record and get the ranking through some PHP code, but I wonder if this is possible with Mysql queries.
I googled but did not really come up with a good solution. Any answer is highly appreciated!
This is the row_number function in postgresql and other databases which unfortunately isn't present in mysql.
This article http://www.mysqltutorial.org/mysql-row_number/ explains how to emulate it in mysql
To adapt the example from it
SET #row_number = 0;
SELECT
(#row_number:=#row_number + 1) AS rank, id, participant_id,exam_id, score
FROM
exams
LIMIT 5;
Following query will give you the required result
select t2.id,t2.exam_id,t2.exam_id,t2.participant_id,t2.score,t2.ranking from
(SELECT t.id,t.exam_id,t.participant_id,t.score,(#num:=#num+1) AS
ranking FROM table1 t cross join (SELECT #num:=0) AS dummy
ORDER BY t.score desc)as t2 where t2.participant_id=15;
You can run a query to create a separate table of results and then add condition to get the rank of required participant.
here is the query you can try
SELECT * FROM (SELECT re.*, #curRow := #curRow + 1 AS rank
FROM results re JOIN (SELECT #curRow := 0) r
WHERE 1 ORDER BY re.`score` DESC) AS tablea
WHERE participant_id=15
here is the result
id exam_id participant_id score rank
1 3 15 343.23 3

Add a Column in Query

Here is my query:
SET #rank=0;
SELECT #rank := #rank +1 AS rank_id, name, SUM(points) AS points
FROM battle_points
WHERE category = 'test'
AND user_id !=0
GROUP BY user_id
ORDER BY points DESC;
I'd like to add a column rank based on the total points. With this query, the points are fine but the rank_id virtual column doesn't match up.
For example, the top user with the most points has rank 26, yet the rank_id column has a value of 24.
How do I matchup the rank_id column with the points column?
Note: while I am fully versed in PHP, I need a solution for MySQL only.
You are on the right path, but you need to put the main query in a subquery so that the ordering occurs before the rank calculation, like so:
SET #rank=0;
SELECT #rank := #rank +1 AS rank_id, mainQ.*
FROM (
SELECT name, SUM(points) AS points
FROM battle_points
WHERE category = 'test'
AND user_id !=0
GROUP BY user_id
ORDER BY points DESC
) AS mainQ
;
Edit: Qualified * to mainQ.*.

mysql ranking based on sum of values

having a mysql table with multiple records belonging many different users like this:
id score
1 , 50
1 , 75
1 , 40
1, 20
2 , 85
2 , 60
2 , 20
i need to get the rank of each id but after finding the sum of their score;
the rank should be the same if the total score for each player is the same.
this gives me the total for each player:
select id,sum(score) as total from table_scores group by id order by total desc;
is it posssible to find the sum like above and use it to rank the players in one query?
Something big missing from the accepted answer. The rank needs to be bumped after a tie. If you've got 2 tied for 3rd place, there is no 4th place.
The following query is an adjustment of the accepted SQL to account for this and reset the rank variable (#r in the query) to match the row value. You can avoid the extra addition in the CASE/WHEN but initializing #row to 1 instead of 0 but then the row value is off by 1 and my OCD won't let that stand even if row number is not valuable.
select
id, total,
CASE WHEN #l=total THEN #r ELSE #r:=#row + 1 END as rank,
#l:=total,
#row:=#row + 1
FROM (
select
id, sum(score) as total
from
table_scores
group by
id
order by
total desc
) totals, (SELECT #r:=0, #row:=0, #l:=NULL) rank;
You can rank rows using variables:
select
id, total,
CASE WHEN #l=total THEN #r ELSE #r:=#r+1 END as rank,
#l:=total
FROM (
select
id, sum(score) as total
from
table_scores
group by
id
order by
total desc
) totals, (SELECT #r:=0, #l:=NULL) rank;
Please see it working here.
i find one more way to this problem... This one is based on JOIN clause
SET #rank = 0;
SELECT t1.id, t1.score, t2.rank
FROM (SELECT id, SUM(score) as score
FROM table_scores GROUP BY id ORDER BY score Desc) AS t1
INNER JOIN
(SELECT x.score, #rank:=#rank + 1 as rank FROM
(SELECT DISTINCT(SUM(score)) AS score
FROM table_scores
GROUP BY id ORDER BY score DESC) AS x) AS t2
ON t1.score = t2.score
Here is SQL Fiddle: http://sqlfiddle.com/#!9/2dcfc/16
P.S. it's interesting to see there is more then one way to solve a problem...

Sum Top 10 Values

I’ve searched and I know this has been asked before but I am struggling to get my head around what I can / can’t do.
My cycling club records race results each time a rider has entered a race. Each result is awarded points - 50 for 1st, 49 for 2nd etc.
So the table looks like
resultid(pk) | riderid(fk) | leaguepts
1 1 50
2 2 49
3 3 48
4 1 50
5 2 42
6 3 50
7 4 30
...etc
I am trying to extract the sum of top 10 points awarded for each riderid from the results table.
(the actual database is a bit more complicated with a table for rider name / rider id and also a race table so we can display the results of each race etc but I just want to get the basic league table query working first of all)
So I want to extract the sum of the top 10 best scores for each rider. Then display each riders score, in a descending league table.
So far I’ve only had success using UNION ALL e.g.
SELECT sum(points) AS pts from
(
SELECT points from `results`
WHERE riderid = 1
ORDER BY points DESC
LIMIT 10
) as riderpts
UNION ALL
SELECT sum(points) AS pts from
(
SELECT points from `results`
WHERE riderid = 2
ORDER BY points DESC
LIMIT 10
) as riderpts
ORDER BY pts DESC
But there could be up to 90-odd riders who have registered at least one score so this query could get very big.
I found this which looks like it should work for me but doesn't. Sum top 5 values in MySQL I changed the column names for my table but it seems to sum all results, not the top 10 for each rider.
Alternatively I could just issue a query for each rider id. Not good I guess?
Subquerying is a problem because I can't limit on the inner query?
Run a job (manual or cron) to update the league table periodically and just display the table results?
Edit (not sure if this is the correct etiquette or I should start a new thread?). Gordon answered the question below but in the meantime I tried to work this out for myself using one of the links below. I could get results that returned the top 10 scores for each rider with the query below
set #riderid = '';
set #riderrow = 1;
select riderid, leaguepts, row_number
from
(
select
riderid,
leaguepts,
#riderrow := if(#riderid = riderid, #riderrow + 1, 1) as row_number,
#riderid := riderid as dummy
from wp_tt_results order by riderid, leaguepts desc
) as x where x.row_number <= 10;
BUT I can't see what I would need to do next to get the sum of top 10 results per riderid?
In MySQL, the easiest way to do this is probably to use variables:
SELECT riderid, sum(points)
FROM (SELECT r.*,
(#rn := if(#r = riderid, #rn + 1,
if(#r := riderid, 1, 1)
)
) as seqnum
FROM results r CROSS JOIN
(SELECT #r := 0, #rn := 0) as wnw
ORDER BY riderid, points DESC
) r
WHERE seqnum <= 10
GROUP BY riderid;

PDO Return highest integer value and no duplicate entries

I am not sure if this question has been asked yet but I don't know how to word it better :)
I have a table with game results. I need to return the top 10 results (for a leaderboard) but my results has to return the highest game per player and not replicate the player (so I am looking for each players highest score and based on that the top 10 players, not say if I am 1st and 3rd on scores to show me twice).
The SQL I am using at the moment is:
SELECT
`games`.`score`,
CONCAT(`users`.`full_name`) AS `name`,
`users`.`facebook_id`
FROM
`games`,
`users`
WHERE
`games`.`user_id`=`users`.`id`
AND `users`.`id` IN ('user ids goes here')
AND `games`.`status`=2
ORDER BY
`games`.`score` DESC
LIMIT 10";
Can this be done with a single query or would it be best to work on the returned array and create my desired results from there?
UPDATE:
To best illustrate, say we have the following results:
Jack - 300 points
Jill - 280 points
Gareth - 250 points
Jack - 240 points (this is the same jack)
Peter - 230 points
....
I want to return from all of the games the top 10 players based on their highest score, so not neccesarily 10 results, but I'm looking for a result which would then return the following:
Jack - 300 points
Jill - 280 points
Gareth - 250 points
Peter - 230 points
So The 2nd Jack is removed because he is already on the list just with a higher score.
I hope this helps :)
Okay, let's do this step by step.
With this query, we get a list of games and their players, sorted by game and score. We add a rownumber which resets to 1 for each game.
SELECT
games.game_id,
`games`.`score`,
CONCAT(`users`.`full_name`) AS `name`,
`users`.`facebook_id`,
#row_num := if(#game != games.game_id, 1, #row_num + 1) AS rownumber,
#game := games.game_id
FROM
`games`
INNER JOIN users ON `games`.`user_id`=`users`.`id`
, (select #row_num := 1, #game := NULL) variables
WHERE
`users`.`id` IN ('user ids goes here')
AND `games`.`status`=2
ORDER BY
games.game_id, /*or something*/
`games`.`score` DESC
Now we can get the top 10 players for each game by putting above query into this
SELECT * FROM (
<above query here>
) subquery_alias
WHERE rownumber <= 10;
Now we just have to add a GROUP BY playername and get the greatest result. So your final query is this:
SELECT name, MAX(score) FROM (
SELECT
games.game_id,
`games`.`score`,
CONCAT(`users`.`full_name`) AS `name`,
`users`.`facebook_id`,
#row_num := if(#game != games.game_id, 1, #row_num + 1) AS rownumber,
#game := games.game_id
FROM
`games`
INNER JOIN users ON `games`.`user_id`=`users`.`id`
, (select #row_num := 1, #game := NULL) variables
WHERE
`users`.`id` IN ('user ids goes here')
AND `games`.`status`=2
ORDER BY
games.game_id, /*or something*/
`games`.`score` DESC
) subquery_alias
WHERE rownumber <= 10
GROUP BY `name`
I think what you need is the following query:
SELECT
MAX(`games`.`score`),
CONCAT(`users`.`full_name`) AS `name`,
`users`.`facebook_id`
FROM
`games`
JOIN `users`
ON `games`.`user_id` = `users`.`id`
WHERE `users`.`id` IN ('user ids goes here')
AND `games`.`status`=2
GROUP BY `name`
ORDER BY `games`.`score` DESC
LIMIT 10";
Just using a simple MAX(games.score) would do the trick along with grouping the result together.