sql >> Database >  >> RDS >> Mysql

Bepaal de rangorde op basis van meerdere kolommen in MySQL

In een afgeleide tabel (subquery binnen de FROM clausule), ordenen we onze gegevens zodanig dat alle rijen dezelfde user_id . hebben waarden komen samen, met verdere sortering tussen hen op basis van game_detail in aflopende volgorde.

Nu gebruiken we deze resultatenset en gebruiken voorwaardelijke CASE..WHEN expressies om de rijnummering te evalueren. Het zal zijn als een Looping-techniek (die we gebruiken in applicatiecode, bijvoorbeeld:PHP). We zouden de vorige rijwaarden opslaan in de door de gebruiker gedefinieerde variabelen en vervolgens de waarde(n) van de huidige rij vergelijken met de vorige rij. Uiteindelijk zullen we het rijnummer dienovereenkomstig toewijzen.

Bewerken: Gebaseerd op MySQL docs en de observatie van @Gordon Linoff:

De volgorde van evaluatie voor expressies met gebruikersvariabelen is niet gedefinieerd. Er is bijvoorbeeld geen garantie dat SELECT @a, @a:[email protected] +1 beoordeelt eerst @a en voert vervolgens de opdracht uit.

We moeten het rijnummer evalueren en de user_id . toewijzen waarde naar @u variabele binnen dezelfde uitdrukking.

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 

Resultaat

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

Bekijken op DB Fiddle

Een interessante opmerking van MySQL Docs , die ik onlangs ontdekte:

Eerdere releases van MySQL maakten het mogelijk om een ​​waarde toe te kennen aan een gebruikersvariabele in andere statements dan SET. Deze functionaliteit wordt ondersteund in MySQL 8.0 voor achterwaartse compatibiliteit, maar kan in een toekomstige versie van MySQL worden verwijderd.

Ook, dankzij een mede SO-lid, kwam ik deze blog tegen van MySQL Team:https://mysqlserverteam.com/row-numbering-ranking-how-to-use-less-user-variables-in-mysql-queries/

Algemene opmerking is dat het gebruik van ORDER BY met evaluatie van de gebruikersvariabelen in hetzelfde queryblok, garandeert niet dat de waarden altijd correct zijn. Zoals, MySQL-optimizer mogelijk op hun plaats komen en onze veronderstelde . veranderen volgorde van evaluatie.

De beste aanpak voor dit probleem zou zijn om te upgraden naar MySQL 8+ en de Row_Number() functionaliteit:

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;

Resultaat

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

Bekijken op DB Fiddle



  1. Lokale tijdelijke tabel in Oracle 10 (voor de reikwijdte van Stored Procedure)

  2. Een nieuwe gebruiker maken en machtigingen verlenen in MySQL

  3. Hoe kan ik een database onder git (versiebeheer) plaatsen?

  4. Alleen maand en jaar krijgen van SQL DATE