Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Determinar a classificação com base em várias colunas no MySQL


Em uma Tabela derivada (subconsulta dentro do FROM cláusula), ordenamos nossos dados de forma que todas as linhas tenham o mesmo user_id os valores se juntam, com classificação adicional entre eles com base em game_detail por ordem decrescente.

Agora, usamos este conjunto de resultados e usamos CASE..WHEN condicional expressões para avaliar a numeração de linha. Será como uma técnica de Looping (que usamos no código da aplicação, por exemplo:PHP). Armazenaríamos os valores da linha anterior nas variáveis ​​definidas pelo usuário e, em seguida, verificaríamos o(s) valor(es) da linha atual em relação à linha anterior. Eventualmente, atribuiremos o número da linha de acordo.

Editar: Baseado em MySQL docs e a observação de @Gordon Linoff:

A ordem de avaliação para expressões envolvendo variáveis ​​de usuário é indefinida. Por exemplo, não há garantia de que SELECT @a, @a:[email protected] +1avalia @a primeiro e depois executa a atribuição.

Precisaremos avaliar o número da linha e atribuir o user_id valor para @u variável dentro da mesma expressão.
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 

Resultado
| 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      |

Visualizar no DB Fiddle

Uma nota interessante do MySQL Docs , que descobri recentemente:

As versões anteriores do MySQL tornaram possível atribuir um valor a uma variável de usuário em instruções diferentes de SET. Esta funcionalidade é suportada no MySQL 8.0 para compatibilidade com versões anteriores, mas está sujeita a remoção em uma versão futura do MySQL.

Além disso, graças a um colega membro do SO, encontrei este blog da MySQL Team:https://mysqlserverteam.com/row-numbering-ranking-how-to-use-less-user-variables-in-mysql-queries/

A observação geral é que usando ORDER BY com avaliação das variáveis ​​do usuário no mesmo bloco de consulta, não garante que os valores estejam sempre corretos. Como, o otimizador MySQL pode entrar em vigor e alterar nossa presumida ordem de avaliação.

A melhor abordagem para esse problema seria atualizar para o MySQL 8+ e utilizar o Row_Number() funcionalidade:

Esquema (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;

Resultado
| 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              |

Visualizar no DB Fiddle