Mysql
 sql >> Base de données >  >> RDS >> Mysql

Déterminer le classement en fonction de plusieurs colonnes dans MySQL

Dans une table dérivée (sous-requête à l'intérieur du FROM clause), nous ordonnons nos données de telle sorte que toutes les lignes ayant le même user_id les valeurs se rejoignent, avec un tri supplémentaire entre elles basé sur game_detail par ordre décroissant.

Maintenant, nous utilisons cet ensemble de résultats et utilisons le conditionnel CASE..WHEN expressions pour évaluer la numérotation des lignes. Ce sera comme une technique de bouclage (que nous utilisons dans le code d'application, par exemple :PHP). Nous stockons les valeurs de la ligne précédente dans les variables définies par l'utilisateur, puis vérifions la ou les valeurs de la ligne actuelle par rapport à la ligne précédente. Finalement, nous attribuerons un numéro de ligne en conséquence.

Modifier : Basé sur MySQL docs et l'observation de @Gordon Linoff :

L'ordre d'évaluation des expressions impliquant des variables utilisateur n'est pas défini. Par exemple, rien ne garantit que SELECT @a, @a :[email protected] +1évalue d'abord @a, puis effectue le devoir.

Nous devrons évaluer le numéro de ligne et attribuer le user_id valeur à @u variable dans la même expression.

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 

Résultat

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

Voir sur DB Fiddle

Une note intéressante de MySQL Docs , que j'ai découvert récemment :

Les versions précédentes de MySQL permettaient d'attribuer une valeur à une variable utilisateur dans des instructions autres que SET. Cette fonctionnalité est prise en charge dans MySQL 8.0 pour la rétrocompatibilité, mais est susceptible d'être supprimée dans une future version de MySQL.

De plus, grâce à un autre membre SO, je suis tombé sur ce blog de l'équipe MySQL :https://mysqlserverteam.com/row-numbering-ranking-how-to-use-less-user-variables-in-mysql-queries/

L'observation générale est que l'utilisation de ORDER BY avec évaluation des variables utilisateur dans le même bloc de requête, ne garantit pas que les valeurs seront toujours correctes. Comme, l'optimiseur MySQL peut entrent en place et changent notre présumé ordre d'évaluation.

La meilleure approche à ce problème serait de mettre à niveau vers MySQL 8+ et d'utiliser le Row_Number() fonctionnalité :

Schéma (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;

Résultat

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

Voir sur DB Fiddle