I am new to sql and I have never used variables or conditions in mysql, but know that from other programming languages. Since a few days I try to find a way to rank a user score. I read a lot of articles, and also questions that asked on stackoverflow and finally I found a solution that nearly does it like I want it.
SELECT
score_users.uid,
score_users.score,
@prev := @curr,
@curr := score,
@rank := IF(@prev = @curr, @rank, @rank +1) AS rank
FROM
score_users,
(SELECT @curr := null, @prev := null, @rank := 0) tmp_tbl
WHERE
score_users.matchday = 1
ORDER BY
score_users.score DESC
But my Problems are tie scores. I don't want to get consecutive ranks, like this:
+------------+------+--------+
| uid | name | rank | score |
+------------+------+--------+
| 4 | Jon | 1 | 20 |
| 1 | Jane | 2 | 19 |
| 2 | Fred | 2 | 19 |
| 9 | July | 3 | 18 |
| 7 | Mary | 4 | 17 |
| 3 | Toni | 5 | 12 |
| 5 | Steve | 5 | 12 |
| 6 | Peter | 6 | 11 |
| 8 | Nina | 7 | 10 |
+------------+------+--------+
I would like to get a result like this:
+------------+------+--------+
| uid | name | rank | score |
+------------+------+--------+
| 4 | Jon | 1 | 20 |
| 1 | Jane | 2 | 19 |
| 2 | Fred | 2 | 19 |
| 9 | July | 4 | 18 |
| 7 | Mary | 5 | 17 |
| 3 | Toni | 6 | 12 |
| 5 | Steve | 6 | 12 |
| 6 | Peter | 8 | 11 |
| 8 | Nina | 9 | 10 |
+------------+------+--------+
I guess I have to create a new temporary table, and some if conditions, but I couldn't find a solution and become desperate! Also, I have to keep an eye on performance, maybe there are better ways to get the rank on score as I did it? I would be very grateful for hints or some code snippet.
You can use another variable to count the same ranks so instead of incrementing @rank
by 1, you increment @rank
by the counter value, like this:
SELECT
score_users.uid,
score_users.score,
@prev := @curr,
@curr := score,
@rank := IF(@prev = @curr, @rank, @rank + @i) AS rank,
IF(@prev <> score, @i:=1, @i:=@i+1) AS counter
FROM
score_users,
(SELECT @curr := null, @prev := null, @rank := 0, @i := 0) tmp_tbl
WHERE
score_users.matchday = 1
ORDER BY
score_users.score DESC
Add another rank field which is always incremented. If the value matches you use your existing rank (not incremented), if not you use your always incremented rank.
SELECT
score_users.uid,
score_users.score,
@prev := @curr,
@curr := score,
@rank1 := @rank1 + 1,
@rank := IF(@prev = @curr, @rank, @rank1) AS rank
FROM
score_users,
(SELECT @curr := null, @prev := null, @rank := 0, @rank1 := 0) tmp_tbl
WHERE
score_users.matchday = 1
ORDER BY
score_users.score DESC
On ties, you may want to skip and use current row num to next unmatched score value row as next rank. Following should help you
SELECT
score_users.uid
, @curr_score := score_users.score as score,
, case when @prev_score = @curr_score then @rank := @rank
else @rank := ( @curr_row + 1 ) -- <- this is what you require
end as rank
, @curr_row := ( @curr_row + 1 ) as curr_row
, @prev_score := @curr_score
FROM
score_users,
( SELECT @curr_score := 0, @prev_score := 0
, @curr_row := 0, @rank := 0 ) initializer
WHERE
score_users.matchday = 1
ORDER BY
score_users.score DESC
来源:https://stackoverflow.com/questions/24118393/mysql-rank-with-ties