Определить рейтинг на основе нескольких столбцов в MySQL - PullRequest
0 голосов
/ 25 ноября 2018

У меня есть таблица, в которой есть 3 поля, я хочу ранжировать столбец на основе user_id и game_id.

Вот SQL Fiddle: http://sqlfiddle.com/#!9/883e9d/1

таблица уже у меня есть:

 user_id | game_id |   game_detial_sum  |
 --------|---------|--------------------|
 6       | 10      |  1000              |   
 6       | 11      |  260               |
 7       | 10      |  1200              |
 7       | 11      |  500               |
 7       | 12      |  360               |
 7       | 13      |  50                | 

ожидаемый результат:

user_id  | game_id |   game_detial_sum  |  user_game_rank  |
 --------|---------|--------------------|------------------|
 6       | 10      |  1000              |   1              |
 6       | 11      |  260               |   2              |
 7       | 10      |  1200              |   1              |
 7       | 11      |  500               |   2              |
 7       | 12      |  360               |   3              |
 7       | 13      |  50                |   4              |

Мои усилия на данный момент:

SET @s := 0; 
SELECT user_id,game_id,game_detail, 
       CASE WHEN user_id = user_id THEN (@s:=@s+1) 
            ELSE @s = 0 
       END As user_game_rank 
FROM game_logs

Редактировать: (От ОП Комментарии ): порядок основан на порядке убывания game_detail

порядка game_detail

Ответы [ 4 ]

0 голосов
/ 27 ноября 2018

Вы можете использовать очень простой коррелированный подзапрос:

SELECT *, (
    SELECT COUNT(DISTINCT game_detail) + 1
    FROM game_logs AS x
    WHERE user_id = t.user_id AND game_detail > t.game_detail
) AS user_game_rank
FROM game_logs AS t
ORDER BY user_id, user_game_rank

DB Fiddle

Это медленнее, но гораздо надежнее, чем пользовательские переменные.Все, что нужно, это одно СОЕДИНЕНИЕ, чтобы сломать их.

0 голосов
/ 25 ноября 2018

В производной таблице (подзапрос внутри предложения FROM) мы упорядочиваем наши данные таким образом, чтобы все строки, имеющие одинаковые значения user_id, объединялись вместе с дальнейшей сортировкой между ними на основе game_detail в порядке убывания.

Теперь мы используем этот набор результатов и используем условные выражения CASE..WHEN для оценки нумерации строк.Это будет похоже на метод Looping (который мы используем в коде приложения, например: PHP).Мы сохраняем значения предыдущей строки в пользовательских переменных, а затем проверяем значения текущей строки по сравнению с предыдущей строкой.В конце концов, мы назначим номер строки соответственно.

Редактировать: На основе MySQL документы и наблюдения @Gordon Linoff:

Порядокоценки для выражений с участием пользовательских переменных не определена.Например, нет гарантии, что SELECT @a, @a: = @ a + 1 сначала вычислит @a, а затем выполнит присваивание.

Нам потребуется оценить номер строки и присвоить user_id значение в @u переменная в том же выражении.

SET @r := 0, @u := 0; 
SELECT
  @r := CASE WHEN @u = dt.user_id 
                  THEN @r + 1
             WHEN @u := dt.user_id /* Notice := instead of = */
                  THEN 1 
        END AS user_game_rank, 
  dt.user_id, 
  dt.game_detail, 
  dt.game_id 

FROM 
( SELECT user_id, game_id, game_detail
  FROM game_logs 
  ORDER BY user_id, game_detail DESC 
) AS dt 

Результат

| user_game_rank | user_id | game_detail | game_id |
| -------------- | ------- | ----------- | ------- |
| 1              | 6       | 260         | 11      |
| 2              | 6       | 100         | 10      |
| 1              | 7       | 1200        | 10      |
| 2              | 7       | 500         | 11      |
| 3              | 7       | 260         | 12      |
| 4              | 7       | 50          | 13      |

Просмотр в DB Fiddle


Интересная заметка из MySQL Docs , которую я обнаружил недавно:

Предыдущие выпуски MySQL позволили назначитьзначение пользовательской переменной в операторах, отличных от SET.Эта функциональность поддерживается в MySQL 8.0 для обратной совместимости, но подлежит удалению в будущем выпуске MySQL.

Кроме того, благодаря товарищу по SO, этот блог натолкнулся на MySQL Team: https://mysqlserverteam.com/row-numbering-ranking-how-to-use-less-user-variables-in-mysql-queries/

Общее наблюдение состоит в том, что использование ORDER BY с оценкой пользовательских переменных в одном и том же блоке запроса не гарантирует, что значения всегда будут правильными.Поскольку оптимизатор MySQL может вступить в действие и изменить наш предполагаемый порядок оценки.

Наилучшим подходом к этой проблеме будет обновление до MySQL 8+ и использованиеRow_Number() функциональность:

Схема (MySQL v8.0)

SELECT user_id, 
       game_id, 
       game_detail, 
       ROW_NUMBER() OVER (PARTITION BY user_id 
                          ORDER BY game_detail DESC) AS user_game_rank 
FROM game_logs 
ORDER BY user_id, user_game_rank;

Результат

| user_id | game_id | game_detail | user_game_rank |
| ------- | ------- | ----------- | -------------- |
| 6       | 11      | 260         | 1              |
| 6       | 10      | 100         | 2              |
| 7       | 10      | 1200        | 1              |
| 7       | 11      | 500         | 2              |
| 7       | 12      | 260         | 3              |
| 7       | 13      | 50          | 4              |

Показать на БД Fiddle

0 голосов
/ 26 ноября 2018

Лучшим решением в MySQL до версии 8.0 является следующее:

select gl.*, 
       (@rn := if(@lastUserId = user_id, @rn + 1,
                  if(@lastUserId := user_id, 1, 1)
                 )
        ) as user_game_rank
from (select gl.*
      from game_logs gl
      order by gl.user_id, gl.game_detail desc
     ) gl cross join
     (select @rn := 0, @lastUserId := 0) params;

Упорядочение выполняется в подзапросе.Это требуется начиная с версии MySQL 5.7.Все переменные присваиваются в одном выражении, поэтому разный порядок вычисления выражений не имеет значения (и MySQL не гарантирует порядок вычисления выражений).

0 голосов
/ 25 ноября 2018
SELECT user_id, game_id, game_detail, 
       CASE WHEN user_id = @lastUserId 
            THEN @rank := @rank + 1 
            ELSE @rank := 1 
       END As user_game_rank,
       @lastUserId := user_id
FROM game_logs
cross join (select @rank := 0, @lastUserId := 0) r
order by user_id, game_detail desc

Демонстрация SQLFiddle

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...