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