问题
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.
回答1:
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
回答2:
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
回答3:
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