简单的MySQL更新排名与领带

时间:2011-12-30 20:59:58

标签: mysql ranking

我试图根据分数存储用户的等级,所有这些都是一个表,并在存在平局时跳过排名。例如:

ID   Score Rank
2    23    1
4    17    2
1    17    2
5    10    4
3    2     5

每次更新用户的分数时,还必须更新整个表的排名,因此在分数更新后,将运行以下查询:

SET @rank=0;
UPDATE users SET rank= @rank:= (@rank+1) ORDER BY score DESC;

但是这不支持关系,或者在关系之后跳过排名数字。

我想在尽可能少的查询和没有连接的情况下实现这种重新排名(因为它们看起来相当耗时)。

我能够通过添加两列 - last_score和tie_build_up - 获得所需的结果,并使用以下代码:

SET @rank=0, @last_score = null, @tie_build_up = 0;
UPDATE users SET
    rank= @rank:= if(@last_score = score, @rank, @rank+@tie_build_up+1),
    tie_build_up= @tie_build_up:= if(@last_score = score, @tie_build_up+1, 0),
    last_score= @last_score:= score, ORDER BY score DESC;

我不想要那些额外的列,但是如果没有它们,我就无法使用单个查询。

有什么想法吗?

感谢。

3 个答案:

答案 0 :(得分:3)

这是另一种解决方案:根本不存储排名! : - )

您可以动态计算它们。

示例:

SELECT id, (@next_rank := IF(@score <> score, 1, 0)) nr, 
           (@score := score) score, (@r := IF(@next_rank = 1, @r + 1, @r)) rank 
FROM rank, (SELECT @r := 0) dummy1
ORDER BY score DESC;

结果:

  +------+----+-------+------+
  | id   | nr | score | rank |
  +------+----+-------+------+
  |    2 |  1 |    23 |    1 |
  |    4 |  1 |    17 |    2 |
  |    1 |  0 |    17 |    2 |
  |    5 |  1 |    10 |    3 |
  |    3 |  1 |     2 |    4 |
  +------+----+-------+------+

nr这里是aт辅助列,指示我们是否应该指定下一个等级。

您可以将此查询包装在另一个select中并执行分页,例如。

SELECT id, score, rank 
FROM (SELECT id, (@next_rank := IF(@score <> score, 1, 0)) nr, 
           (@score := score) score, (@r := IF(@next_rank = 1, @r + 1, @r)) rank
      FROM rank, (SELECT @r := 0) dummy1
      ORDER BY score DESC) t
      WHERE rank > 1 and rank < 3;

结果:

  +------+-------+------+
  | id   | score | rank |
  +------+-------+------+
  |    4 |    17 |    2 |
  |    1 |    17 |    2 |
  +------+-------+------+

注意:由于现在rank是一个计算列,因此您无法对其进行索引并有效地将数据页远远地分页到数据集中(即“选择排名为3000到3010的记录” )。但它仍然适用于“选择前N名”(前提是您在查询中添加了相应的LIMIT

答案 1 :(得分:0)

我确信你有充分的理由选择这个设计,但我认为你应该将排名从数据库中排除。对于一个用户的分数中的每个更改,更新整个表可能会导致几乎任何大小的表都出现非常严重的性能问题。我建议你重新考虑这个选择。我建议简单地按分数对表进行排序,并在应用程序代码中分配排名。

答案 2 :(得分:-1)

我按以下方式计算排名和位置:

更新并获取我需要的值,我首先添加它们,添加一个额外的1并减去原始值。这样我就不需要表格内的任何帮助栏;

SET @rank=0;
SET @position=0;
SET @last_points=null;

UPDATE tip_invitation 
set 
    rank = @rank:=if(@last_points = points, @rank, @rank + 1),
    position = ((@last_points := points)-points) + (@position := @position+1)
where
    tippgemeinschaft_id = 1 ORDER BY points DESC;