MySQL带并列排名

7

我是SQL新手,以前没有在MySQL中使用过变量或条件语句,但是从其他编程语言了解过。最近几天,我一直在寻找一种方法来对用户得分进行排名。我阅读了很多文章,也看了stackoverflow上的问题,最终我找到了一个几乎符合我的要求的解决方案。

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

但是我的问题是平局。我不想获得连续的排名,就像这样:
+------------+------+--------+
| 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   |
+------------+------+--------+

我希望能够得到以下结果:
+------------+------+--------+
| 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   |
+------------+------+--------+

我想我需要创建一个新的临时表和一些if条件,但是我找不到解决方案,变得绝望了! 另外,我还要关注性能,也许有更好的方法来获取分数排名,比如我现在做的这种方式?如果您能给我一些提示或者代码片段,我将非常感激。


我会在SQL之外生成排名,因为使用任何程序都更容易(按分数排名就足够了)。在MySQL中做你正在做的事情很复杂! - serakfalcon
3个回答

4

您可以使用另一个变量来计算相同的等级,因此不要将@rank增加1,而是将@rank增加计数器的值,例如:

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
谢谢Aziz,这个很好用。我只是在子查询中将@rank设置为1,否则排名会从0开始!干杯 - FredFus

0

添加另一个排名字段,该字段始终递增。如果值匹配,则使用现有的排名(不递增),否则使用始终递增的排名。

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

0

在处理并列情况时,您可能希望跳过当前行,并将当前行号用作下一个不匹配的分数值行的排名。 以下内容应该会对您有所帮助。

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

我正在做类似的事情,但是在第二个并列值前面加了一个“T” - 2,T2。使用WHEN @prev_val:= score then concat('T',@count-1)可以工作 - 但如果有3个并列值,则不会出现2,T2,T2 - 而是得到2,T2,T3。有什么想法如何实现这一点吗? - user3741598

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接