In una tabella derivata
(sottoquery all'interno di FROM
clausola), ordiniamo i nostri dati in modo tale che tutte le righe abbiano lo stesso user_id
i valori si uniscono, con un ulteriore ordinamento tra di loro in base a game_detail
in ordine decrescente.
Ora usiamo questo set di risultati e usiamo il condizionale CASE..WHEN
espressioni per valutare la numerazione delle righe. Sarà come una tecnica di Looping (che usiamo nel codice dell'applicazione, ad esempio:PHP). Memorizziamo i valori della riga precedente nelle variabili definite dall'utente e quindi controlliamo i valori della riga corrente rispetto alla riga precedente. Alla fine, assegneremo il numero di riga di conseguenza.
Modifica: Basato su MySQL docs e l'osservazione di @Gordon Linoff:
L'ordine di valutazione per le espressioni che coinvolgono variabili utente non è definito. Ad esempio, non vi è alcuna garanzia che SELECT @a, @a:[email protected] +1valuta prima @a e poi esegue il compito.
Dovremo valutare il numero di riga e assegnare il user_id
valore a @u
variabile all'interno della stessa espressione.
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
Risultato
| 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 |
Una nota interessante da MySQL Documenti , che ho scoperto di recente:
Le versioni precedenti di MySQL consentivano di assegnare un valore a una variabile utente in istruzioni diverse da SET. Questa funzionalità è supportata in MySQL 8.0 per la compatibilità con le versioni precedenti, ma è soggetta a rimozione in una versione futura di MySQL.
Inoltre, grazie a un altro membro di SO, mi sono imbattuto in questo blog di MySQL Team:https://mysqlserverteam.com/row-numbering-ranking-how-to-use-less-user-variables-in-mysql-queries/
L'osservazione generale è che usando ORDER BY
con la valutazione delle variabili utente nello stesso blocco di query, non garantisce che i valori siano sempre corretti. Come, l'ottimizzatore MySQL può entra in atto e cambia il nostro presunto ordine di valutazione.
Il miglior approccio a questo problema sarebbe aggiornare a MySQL 8+ e utilizzare Row_Number()
funzionalità:
Schema (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;
Risultato
| 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 |