Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Bestimmen Sie den Rang basierend auf mehreren Spalten in MySQL

In einer abgeleiteten Tabelle (Unterabfrage innerhalb des FROM -Klausel), ordnen wir unsere Daten so an, dass alle Zeilen dieselbe user_id haben Werte kommen zusammen, mit weiterer Sortierung zwischen ihnen basierend auf game_detail in absteigender Reihenfolge.

Jetzt verwenden wir diese Ergebnismenge und verwenden das bedingte CASE..WHEN Ausdrücke, um die Zeilennummerierung auszuwerten. Es wird wie eine Schleifentechnik sein (die wir im Anwendungscode verwenden, zB:PHP). Wir würden die vorherigen Zeilenwerte in den benutzerdefinierten Variablen speichern und dann den/die Wert(e) der aktuellen Zeile mit der vorherigen Zeile vergleichen. Schließlich werden wir die Zeilennummer entsprechend zuweisen.

Bearbeiten: Basierend auf MySQL docs und @Gordon Linoffs Beobachtung:

Die Auswertungsreihenfolge für Ausdrücke mit Benutzervariablen ist nicht definiert. Beispielsweise gibt es keine Garantie dafür, dass SELECT @a, @a:[email protected] +1wertet zuerst @a aus und führt dann die Zuweisung durch.

Wir müssen die Zeilennummer auswerten und die user_id zuweisen Wert zu @u Variable innerhalb desselben Ausdrucks.

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 

Ergebnis

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

Ansicht auf DB Fiddle

Ein interessanter Hinweis von MySQL Docs , die ich kürzlich entdeckt habe:

Frühere Versionen von MySQL ermöglichten es, einer benutzer-Variablen in anderen Anweisungen als SET einen Wert zuzuweisen. Diese Funktionalität wird in MySQL 8.0 aus Gründen der Abwärtskompatibilität unterstützt, kann aber in einer zukünftigen Version von MySQL entfernt werden.

Außerdem bin ich dank eines anderen SO-Mitglieds auf diesen Blog des MySQL-Teams gestoßen:https://mysqlserverteam.com/row-numbering-ranking-how-to-use-less-user-variables-in-mysql-queries/

Allgemeine Beobachtung ist die Verwendung von ORDER BY B. mit Auswertung der Benutzervariablen im selben Abfrageblock, stellt nicht sicher, dass die Werte immer korrekt sind. Wie der MySQL-Optimierer darf eintreten und unsere vermutete ändern Reihenfolge der Auswertung.

Der beste Ansatz für dieses Problem wäre ein Upgrade auf MySQL 8+ und die Verwendung der Row_Number() Funktionalität:

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;

Ergebnis

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

Ansicht auf DB Fiddle