Простое обновление ранга MySQL со связями - PullRequest
3 голосов
/ 31 декабря 2011

Я пытаюсь сохранить рейтинг пользователей на основе оценки, все это в одной таблице, и пропускает ранги, когда есть связь. Например:

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 ]

3 голосов
/ 31 декабря 2011

Вот альтернативное решение: вообще не хранить ранги!: -)

Вы можете рассчитать их на лету.

Пример:

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 в запросе)

0 голосов
/ 31 мая 2014

Я рассчитал ранг и позицию следующим образом:

чтобы обновить И получить нужные мне значения, я сначала добавил их, добавил еще 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;
0 голосов
/ 31 декабря 2011

Я уверен, что у вас есть веская причина для такого выбора дизайна, но я думаю, что вы должны оставить все вместе в базе данных. Обновление всей таблицы для каждого изменения оценки одного пользователя может привести к очень серьезным проблемам с производительностью практически любого размера таблицы. Я предлагаю вам пересмотреть этот выбор. Я бы посоветовал просто отсортировать таблицу по баллам и присвоить ранги в коде приложения.

...