sql >> Base de Datos >  >> RDS >> Mysql

Determinar el rango basado en múltiples columnas en MySQL

En una Tabla Derivada (subconsulta dentro del FROM cláusula), ordenamos nuestros datos de manera que todas las filas tengan el mismo user_id los valores se unen, con una mayor clasificación entre ellos según game_detail en orden descendente.

Ahora, usamos este conjunto de resultados y usamos CASE..WHEN condicional expresiones para evaluar la numeración de filas. Será como una técnica de bucle (que usamos en el código de la aplicación, por ejemplo:PHP). Almacenaríamos los valores de la fila anterior en las variables definidas por el usuario y luego compararíamos los valores de la fila actual con la fila anterior. Eventualmente, asignaremos el número de fila correspondiente.

Editar: Basado en MySQL docs y la observación de @Gordon Linoff:

El orden de evaluación de las expresiones que implican variables de usuario no está definido. Por ejemplo, no hay garantía de que SELECT @a, @a:[email protected] +1 evalúa @a primero y luego realiza la tarea.

Tendremos que evaluar el número de fila y asignar el user_id valor a @u variable dentro de la misma expresión.

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      |

Ver en DB Fiddle

Una nota interesante de MySQL Docs , que descubrí recientemente:

Las versiones anteriores de MySQL hicieron posible asignar un valor a una variable de usuario en declaraciones que no sean SET. Esta funcionalidad es compatible con MySQL 8.0 por compatibilidad con versiones anteriores, pero está sujeta a eliminación en una versión futura de MySQL.

Además, gracias a otro miembro de SO, encontré este blog del equipo de MySQL:https://mysqlserverteam.com/row-numbering-ranking-how-to-use-less-user-variables-in-mysql-queries/

La observación general es que usar ORDER BY con la evaluación de las variables de usuario en el mismo bloque de consulta, no garantiza que los valores sean siempre correctos. Como, el optimizador de MySQL puede entrar en su lugar y cambiar nuestro presunto orden de evaluación.

El mejor enfoque para este problema sería actualizar a MySQL 8+ y utilizar Row_Number() funcionalidad:

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              |

Ver en DB Fiddle