MySQL Rank with ties

柔情痞子 提交于 2019-12-18 04:53:30

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!