Determine Rank based on Multiple Columns in MySQL

  • A+
Category:Languages

I have a table which has 3 fields, I want to rank column based on user_id and game_id.

Here is SQL Fiddle : http://sqlfiddle.com/#!9/883e9d/1

the table already I have :

 user_id | game_id |   game_detial_sum  |  --------|---------|--------------------|  6       | 10      |  1000              |     6       | 11      |  260               |  7       | 10      |  1200              |  7       | 11      |  500               |  7       | 12      |  360               |  7       | 13      |  50                |  

expected output :

user_id  | game_id |   game_detial_sum  |  user_game_rank  |  --------|---------|--------------------|------------------|  6       | 10      |  1000              |   1              |  6       | 11      |  260               |   2              |  7       | 10      |  1200              |   1              |  7       | 11      |  500               |   2              |  7       | 12      |  360               |   3              |  7       | 13      |  50                |   4              | 

My efforts so far :

SET @s := 0;  SELECT user_id,game_id,game_detail,         CASE WHEN user_id = user_id THEN (@s:=@s+1)              ELSE @s = 0         END As user_game_rank  FROM game_logs 

Edit: (From OP Comments): Ordering is based on the descending order of game_detail

order of game_detail

 


In a Derived Table (subquery inside the FROM clause), we order our data such that all the rows having same user_id values come together, with further sorting between them based on game_detail in Descending order.

Now, we use this result-set and use conditional CASE..WHEN expressions to evaluate the row numbering. It will be like a Looping technique (which we use in application code, eg: PHP). We would store the previous row values in the User-defined variables, and then check the current row's value(s) against the previous row. Eventually, we will assign row number accordingly.

SET @r := 0, @u := 0;  SELECT   @r := CASE WHEN @u = dt.user_id THEN @r + 1              ELSE 1         END AS user_game_rank,    @u := dt.user_id AS 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  

Result

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

View on DB Fiddle


An interesting note from MySQL Docs, which I discovered recently:

Previous releases of MySQL made it possible to assign a value to a user variable in statements other than SET. This functionality is supported in MySQL 8.0 for backward compatibility but is subject to removal in a future release of MySQL.

Also, thanks to a fellow SO member, came across this blog by MySQL Team: https://mysqlserverteam.com/row-numbering-ranking-how-to-use-less-user-variables-in-mysql-queries/

General observation is that using ORDER BY with evaluation of the user variables in the same query block, does not ensure that the values will be correct always. As, MySQL optimizer may come into place and change our presumed order of evaluation.

Best approach to this problem would be to upgrade to MySQL 8+ and utilize the Row_Number() functionality:

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; 

Result

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

View on DB Fiddle

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: