Determine Rank based on Multiple Columns in MySQL
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
mysql sql sql-rank
add a comment |
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
mysql sql sql-rank
Is the ordering based on ascending order ofgame_idor the descending order ofgame_detail?
– Madhur Bhaiya
Nov 25 '18 at 6:15
order of game_detail
– ha͞me̸d̨
Nov 25 '18 at 6:17
add a comment |
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
mysql sql sql-rank
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
mysql sql sql-rank
mysql sql sql-rank
edited Nov 27 '18 at 15:11
Salman A
183k66340438
183k66340438
asked Nov 25 '18 at 6:11
ha͞me̸d̨ha͞me̸d̨
695
695
Is the ordering based on ascending order ofgame_idor the descending order ofgame_detail?
– Madhur Bhaiya
Nov 25 '18 at 6:15
order of game_detail
– ha͞me̸d̨
Nov 25 '18 at 6:17
add a comment |
Is the ordering based on ascending order ofgame_idor the descending order ofgame_detail?
– Madhur Bhaiya
Nov 25 '18 at 6:15
order of game_detail
– ha͞me̸d̨
Nov 25 '18 at 6:17
Is the ordering based on ascending order of
game_id or the descending order of game_detail ?– Madhur Bhaiya
Nov 25 '18 at 6:15
Is the ordering based on ascending order of
game_id or the descending order of game_detail ?– Madhur Bhaiya
Nov 25 '18 at 6:15
order of game_detail
– ha͞me̸d̨
Nov 25 '18 at 6:17
order of game_detail
– ha͞me̸d̨
Nov 25 '18 at 6:17
add a comment |
4 Answers
4
active
oldest
votes
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.
Edit: Based on MySQL docs and @Gordon Linoff's observation:
The order of evaluation for expressions involving user variables is
undefined. For example, there is no guarantee that SELECT @a, @a:=@a+1
evaluates @a first and then performs the assignment.
We will need to evaluate row number and assign the user_id value to @u variable within the same 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
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
add a comment |
The best solution in MySQL, prior to version 8.0 is the following:
select gl.*,
(@rn := if(@lastUserId = user_id, @rn + 1,
if(@lastUserId := user_id, 1, 1)
)
) as user_game_rank
from (select gl.*
from game_logs gl
order by gl.user_id, gl.game_detail desc
) gl cross join
(select @rn := 0, @lastUserId := 0) params;
The ordering is done in a subquery. This is required starting around MySQL 5.7. The variable assignments are all in one expression, so different order of evaluation of expressions doesn't matter (and MySQL doesn't guarantee the order of evaluation of expressions).
@lastUserId := user_idcan still be evaluated before theif()expression. definitely some trick involved inif(user_id := @lastUserId, but unable to understand so. How does that work ? Some explanation would be handy.
– Madhur Bhaiya
Nov 26 '18 at 12:08
1
@MadhurBhaiya . . . That line was a mistake and has been removed. It is not used in the calculation.
– Gordon Linoff
Nov 26 '18 at 12:31
Makes sense now. +1
– Madhur Bhaiya
Nov 26 '18 at 12:31
add a comment |
SELECT user_id, game_id, game_detail,
CASE WHEN user_id = @lastUserId
THEN @rank := @rank + 1
ELSE @rank := 1
END As user_game_rank,
@lastUserId := user_id
FROM game_logs
cross join (select @rank := 0, @lastUserId := 0) r
order by user_id, game_detail desc
SQLFiddle Demo
@GordonLinoff particular blog from MySQL team may be helpful: mysqlserverteam.com/… 2) This answer is wrong because it does not determine the row numbering based ongame_detailin descending order. It just seem to work, because (unfortunately) OP's sample data itself is insufficient (it is already sorted). Thirdly, afaik, evaluation of user variables in this and the other answer (mine) is happening in two different expressions (separated by comma). Will be happy if specific difference can be showcased
– Madhur Bhaiya
Nov 25 '18 at 14:55
@MadhurBhaiya: I added anorder by
– juergen d
Nov 25 '18 at 16:30
@juergend I will also ask you to read this blog once: mysqlserverteam.com/… Basically MySQL does not guarantee whether evaluation will happen beforeorder byor after (due to its own optimization kicking in).
– Madhur Bhaiya
Nov 25 '18 at 16:32
@juergend I had a chat with Nick about this kind of problems, and unexpected behaviour in certain scenarios etc, in comments of another question. You may be interested in the same: stackoverflow.com/questions/53404473/…
– Madhur Bhaiya
Nov 25 '18 at 16:35
@MadhurBhaiya . . . You're right. This makes the same mistake yours does.
– Gordon Linoff
Nov 25 '18 at 21:45
|
show 1 more comment
You can use a very simple correlated sub query:
SELECT *, (
SELECT COUNT(DISTINCT game_detail) + 1
FROM game_logs AS x
WHERE user_id = t.user_id AND game_detail > t.game_detail
) AS user_game_rank
FROM game_logs AS t
ORDER BY user_id, user_game_rank
DB Fiddle
It is slower but far more reliable than user variables. All it takes is one JOIN to break them.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53465111%2fdetermine-rank-based-on-multiple-columns-in-mysql%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
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.
Edit: Based on MySQL docs and @Gordon Linoff's observation:
The order of evaluation for expressions involving user variables is
undefined. For example, there is no guarantee that SELECT @a, @a:=@a+1
evaluates @a first and then performs the assignment.
We will need to evaluate row number and assign the user_id value to @u variable within the same 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
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
add a comment |
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.
Edit: Based on MySQL docs and @Gordon Linoff's observation:
The order of evaluation for expressions involving user variables is
undefined. For example, there is no guarantee that SELECT @a, @a:=@a+1
evaluates @a first and then performs the assignment.
We will need to evaluate row number and assign the user_id value to @u variable within the same 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
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
add a comment |
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.
Edit: Based on MySQL docs and @Gordon Linoff's observation:
The order of evaluation for expressions involving user variables is
undefined. For example, there is no guarantee that SELECT @a, @a:=@a+1
evaluates @a first and then performs the assignment.
We will need to evaluate row number and assign the user_id value to @u variable within the same 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
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
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.
Edit: Based on MySQL docs and @Gordon Linoff's observation:
The order of evaluation for expressions involving user variables is
undefined. For example, there is no guarantee that SELECT @a, @a:=@a+1
evaluates @a first and then performs the assignment.
We will need to evaluate row number and assign the user_id value to @u variable within the same 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
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
edited Nov 26 '18 at 12:50
answered Nov 25 '18 at 6:17
Madhur BhaiyaMadhur Bhaiya
19.6k62236
19.6k62236
add a comment |
add a comment |
The best solution in MySQL, prior to version 8.0 is the following:
select gl.*,
(@rn := if(@lastUserId = user_id, @rn + 1,
if(@lastUserId := user_id, 1, 1)
)
) as user_game_rank
from (select gl.*
from game_logs gl
order by gl.user_id, gl.game_detail desc
) gl cross join
(select @rn := 0, @lastUserId := 0) params;
The ordering is done in a subquery. This is required starting around MySQL 5.7. The variable assignments are all in one expression, so different order of evaluation of expressions doesn't matter (and MySQL doesn't guarantee the order of evaluation of expressions).
@lastUserId := user_idcan still be evaluated before theif()expression. definitely some trick involved inif(user_id := @lastUserId, but unable to understand so. How does that work ? Some explanation would be handy.
– Madhur Bhaiya
Nov 26 '18 at 12:08
1
@MadhurBhaiya . . . That line was a mistake and has been removed. It is not used in the calculation.
– Gordon Linoff
Nov 26 '18 at 12:31
Makes sense now. +1
– Madhur Bhaiya
Nov 26 '18 at 12:31
add a comment |
The best solution in MySQL, prior to version 8.0 is the following:
select gl.*,
(@rn := if(@lastUserId = user_id, @rn + 1,
if(@lastUserId := user_id, 1, 1)
)
) as user_game_rank
from (select gl.*
from game_logs gl
order by gl.user_id, gl.game_detail desc
) gl cross join
(select @rn := 0, @lastUserId := 0) params;
The ordering is done in a subquery. This is required starting around MySQL 5.7. The variable assignments are all in one expression, so different order of evaluation of expressions doesn't matter (and MySQL doesn't guarantee the order of evaluation of expressions).
@lastUserId := user_idcan still be evaluated before theif()expression. definitely some trick involved inif(user_id := @lastUserId, but unable to understand so. How does that work ? Some explanation would be handy.
– Madhur Bhaiya
Nov 26 '18 at 12:08
1
@MadhurBhaiya . . . That line was a mistake and has been removed. It is not used in the calculation.
– Gordon Linoff
Nov 26 '18 at 12:31
Makes sense now. +1
– Madhur Bhaiya
Nov 26 '18 at 12:31
add a comment |
The best solution in MySQL, prior to version 8.0 is the following:
select gl.*,
(@rn := if(@lastUserId = user_id, @rn + 1,
if(@lastUserId := user_id, 1, 1)
)
) as user_game_rank
from (select gl.*
from game_logs gl
order by gl.user_id, gl.game_detail desc
) gl cross join
(select @rn := 0, @lastUserId := 0) params;
The ordering is done in a subquery. This is required starting around MySQL 5.7. The variable assignments are all in one expression, so different order of evaluation of expressions doesn't matter (and MySQL doesn't guarantee the order of evaluation of expressions).
The best solution in MySQL, prior to version 8.0 is the following:
select gl.*,
(@rn := if(@lastUserId = user_id, @rn + 1,
if(@lastUserId := user_id, 1, 1)
)
) as user_game_rank
from (select gl.*
from game_logs gl
order by gl.user_id, gl.game_detail desc
) gl cross join
(select @rn := 0, @lastUserId := 0) params;
The ordering is done in a subquery. This is required starting around MySQL 5.7. The variable assignments are all in one expression, so different order of evaluation of expressions doesn't matter (and MySQL doesn't guarantee the order of evaluation of expressions).
edited Nov 26 '18 at 12:30
answered Nov 25 '18 at 21:46
Gordon LinoffGordon Linoff
784k35310415
784k35310415
@lastUserId := user_idcan still be evaluated before theif()expression. definitely some trick involved inif(user_id := @lastUserId, but unable to understand so. How does that work ? Some explanation would be handy.
– Madhur Bhaiya
Nov 26 '18 at 12:08
1
@MadhurBhaiya . . . That line was a mistake and has been removed. It is not used in the calculation.
– Gordon Linoff
Nov 26 '18 at 12:31
Makes sense now. +1
– Madhur Bhaiya
Nov 26 '18 at 12:31
add a comment |
@lastUserId := user_idcan still be evaluated before theif()expression. definitely some trick involved inif(user_id := @lastUserId, but unable to understand so. How does that work ? Some explanation would be handy.
– Madhur Bhaiya
Nov 26 '18 at 12:08
1
@MadhurBhaiya . . . That line was a mistake and has been removed. It is not used in the calculation.
– Gordon Linoff
Nov 26 '18 at 12:31
Makes sense now. +1
– Madhur Bhaiya
Nov 26 '18 at 12:31
@lastUserId := user_id can still be evaluated before the if() expression. definitely some trick involved in if(user_id := @lastUserId, but unable to understand so. How does that work ? Some explanation would be handy.– Madhur Bhaiya
Nov 26 '18 at 12:08
@lastUserId := user_id can still be evaluated before the if() expression. definitely some trick involved in if(user_id := @lastUserId, but unable to understand so. How does that work ? Some explanation would be handy.– Madhur Bhaiya
Nov 26 '18 at 12:08
1
1
@MadhurBhaiya . . . That line was a mistake and has been removed. It is not used in the calculation.
– Gordon Linoff
Nov 26 '18 at 12:31
@MadhurBhaiya . . . That line was a mistake and has been removed. It is not used in the calculation.
– Gordon Linoff
Nov 26 '18 at 12:31
Makes sense now. +1
– Madhur Bhaiya
Nov 26 '18 at 12:31
Makes sense now. +1
– Madhur Bhaiya
Nov 26 '18 at 12:31
add a comment |
SELECT user_id, game_id, game_detail,
CASE WHEN user_id = @lastUserId
THEN @rank := @rank + 1
ELSE @rank := 1
END As user_game_rank,
@lastUserId := user_id
FROM game_logs
cross join (select @rank := 0, @lastUserId := 0) r
order by user_id, game_detail desc
SQLFiddle Demo
@GordonLinoff particular blog from MySQL team may be helpful: mysqlserverteam.com/… 2) This answer is wrong because it does not determine the row numbering based ongame_detailin descending order. It just seem to work, because (unfortunately) OP's sample data itself is insufficient (it is already sorted). Thirdly, afaik, evaluation of user variables in this and the other answer (mine) is happening in two different expressions (separated by comma). Will be happy if specific difference can be showcased
– Madhur Bhaiya
Nov 25 '18 at 14:55
@MadhurBhaiya: I added anorder by
– juergen d
Nov 25 '18 at 16:30
@juergend I will also ask you to read this blog once: mysqlserverteam.com/… Basically MySQL does not guarantee whether evaluation will happen beforeorder byor after (due to its own optimization kicking in).
– Madhur Bhaiya
Nov 25 '18 at 16:32
@juergend I had a chat with Nick about this kind of problems, and unexpected behaviour in certain scenarios etc, in comments of another question. You may be interested in the same: stackoverflow.com/questions/53404473/…
– Madhur Bhaiya
Nov 25 '18 at 16:35
@MadhurBhaiya . . . You're right. This makes the same mistake yours does.
– Gordon Linoff
Nov 25 '18 at 21:45
|
show 1 more comment
SELECT user_id, game_id, game_detail,
CASE WHEN user_id = @lastUserId
THEN @rank := @rank + 1
ELSE @rank := 1
END As user_game_rank,
@lastUserId := user_id
FROM game_logs
cross join (select @rank := 0, @lastUserId := 0) r
order by user_id, game_detail desc
SQLFiddle Demo
@GordonLinoff particular blog from MySQL team may be helpful: mysqlserverteam.com/… 2) This answer is wrong because it does not determine the row numbering based ongame_detailin descending order. It just seem to work, because (unfortunately) OP's sample data itself is insufficient (it is already sorted). Thirdly, afaik, evaluation of user variables in this and the other answer (mine) is happening in two different expressions (separated by comma). Will be happy if specific difference can be showcased
– Madhur Bhaiya
Nov 25 '18 at 14:55
@MadhurBhaiya: I added anorder by
– juergen d
Nov 25 '18 at 16:30
@juergend I will also ask you to read this blog once: mysqlserverteam.com/… Basically MySQL does not guarantee whether evaluation will happen beforeorder byor after (due to its own optimization kicking in).
– Madhur Bhaiya
Nov 25 '18 at 16:32
@juergend I had a chat with Nick about this kind of problems, and unexpected behaviour in certain scenarios etc, in comments of another question. You may be interested in the same: stackoverflow.com/questions/53404473/…
– Madhur Bhaiya
Nov 25 '18 at 16:35
@MadhurBhaiya . . . You're right. This makes the same mistake yours does.
– Gordon Linoff
Nov 25 '18 at 21:45
|
show 1 more comment
SELECT user_id, game_id, game_detail,
CASE WHEN user_id = @lastUserId
THEN @rank := @rank + 1
ELSE @rank := 1
END As user_game_rank,
@lastUserId := user_id
FROM game_logs
cross join (select @rank := 0, @lastUserId := 0) r
order by user_id, game_detail desc
SQLFiddle Demo
SELECT user_id, game_id, game_detail,
CASE WHEN user_id = @lastUserId
THEN @rank := @rank + 1
ELSE @rank := 1
END As user_game_rank,
@lastUserId := user_id
FROM game_logs
cross join (select @rank := 0, @lastUserId := 0) r
order by user_id, game_detail desc
SQLFiddle Demo
edited Nov 25 '18 at 16:30
answered Nov 25 '18 at 6:16
juergen djuergen d
161k24204264
161k24204264
@GordonLinoff particular blog from MySQL team may be helpful: mysqlserverteam.com/… 2) This answer is wrong because it does not determine the row numbering based ongame_detailin descending order. It just seem to work, because (unfortunately) OP's sample data itself is insufficient (it is already sorted). Thirdly, afaik, evaluation of user variables in this and the other answer (mine) is happening in two different expressions (separated by comma). Will be happy if specific difference can be showcased
– Madhur Bhaiya
Nov 25 '18 at 14:55
@MadhurBhaiya: I added anorder by
– juergen d
Nov 25 '18 at 16:30
@juergend I will also ask you to read this blog once: mysqlserverteam.com/… Basically MySQL does not guarantee whether evaluation will happen beforeorder byor after (due to its own optimization kicking in).
– Madhur Bhaiya
Nov 25 '18 at 16:32
@juergend I had a chat with Nick about this kind of problems, and unexpected behaviour in certain scenarios etc, in comments of another question. You may be interested in the same: stackoverflow.com/questions/53404473/…
– Madhur Bhaiya
Nov 25 '18 at 16:35
@MadhurBhaiya . . . You're right. This makes the same mistake yours does.
– Gordon Linoff
Nov 25 '18 at 21:45
|
show 1 more comment
@GordonLinoff particular blog from MySQL team may be helpful: mysqlserverteam.com/… 2) This answer is wrong because it does not determine the row numbering based ongame_detailin descending order. It just seem to work, because (unfortunately) OP's sample data itself is insufficient (it is already sorted). Thirdly, afaik, evaluation of user variables in this and the other answer (mine) is happening in two different expressions (separated by comma). Will be happy if specific difference can be showcased
– Madhur Bhaiya
Nov 25 '18 at 14:55
@MadhurBhaiya: I added anorder by
– juergen d
Nov 25 '18 at 16:30
@juergend I will also ask you to read this blog once: mysqlserverteam.com/… Basically MySQL does not guarantee whether evaluation will happen beforeorder byor after (due to its own optimization kicking in).
– Madhur Bhaiya
Nov 25 '18 at 16:32
@juergend I had a chat with Nick about this kind of problems, and unexpected behaviour in certain scenarios etc, in comments of another question. You may be interested in the same: stackoverflow.com/questions/53404473/…
– Madhur Bhaiya
Nov 25 '18 at 16:35
@MadhurBhaiya . . . You're right. This makes the same mistake yours does.
– Gordon Linoff
Nov 25 '18 at 21:45
@GordonLinoff particular blog from MySQL team may be helpful: mysqlserverteam.com/… 2) This answer is wrong because it does not determine the row numbering based on
game_detail in descending order. It just seem to work, because (unfortunately) OP's sample data itself is insufficient (it is already sorted). Thirdly, afaik, evaluation of user variables in this and the other answer (mine) is happening in two different expressions (separated by comma). Will be happy if specific difference can be showcased– Madhur Bhaiya
Nov 25 '18 at 14:55
@GordonLinoff particular blog from MySQL team may be helpful: mysqlserverteam.com/… 2) This answer is wrong because it does not determine the row numbering based on
game_detail in descending order. It just seem to work, because (unfortunately) OP's sample data itself is insufficient (it is already sorted). Thirdly, afaik, evaluation of user variables in this and the other answer (mine) is happening in two different expressions (separated by comma). Will be happy if specific difference can be showcased– Madhur Bhaiya
Nov 25 '18 at 14:55
@MadhurBhaiya: I added an
order by– juergen d
Nov 25 '18 at 16:30
@MadhurBhaiya: I added an
order by– juergen d
Nov 25 '18 at 16:30
@juergend I will also ask you to read this blog once: mysqlserverteam.com/… Basically MySQL does not guarantee whether evaluation will happen before
order by or after (due to its own optimization kicking in).– Madhur Bhaiya
Nov 25 '18 at 16:32
@juergend I will also ask you to read this blog once: mysqlserverteam.com/… Basically MySQL does not guarantee whether evaluation will happen before
order by or after (due to its own optimization kicking in).– Madhur Bhaiya
Nov 25 '18 at 16:32
@juergend I had a chat with Nick about this kind of problems, and unexpected behaviour in certain scenarios etc, in comments of another question. You may be interested in the same: stackoverflow.com/questions/53404473/…
– Madhur Bhaiya
Nov 25 '18 at 16:35
@juergend I had a chat with Nick about this kind of problems, and unexpected behaviour in certain scenarios etc, in comments of another question. You may be interested in the same: stackoverflow.com/questions/53404473/…
– Madhur Bhaiya
Nov 25 '18 at 16:35
@MadhurBhaiya . . . You're right. This makes the same mistake yours does.
– Gordon Linoff
Nov 25 '18 at 21:45
@MadhurBhaiya . . . You're right. This makes the same mistake yours does.
– Gordon Linoff
Nov 25 '18 at 21:45
|
show 1 more comment
You can use a very simple correlated sub query:
SELECT *, (
SELECT COUNT(DISTINCT game_detail) + 1
FROM game_logs AS x
WHERE user_id = t.user_id AND game_detail > t.game_detail
) AS user_game_rank
FROM game_logs AS t
ORDER BY user_id, user_game_rank
DB Fiddle
It is slower but far more reliable than user variables. All it takes is one JOIN to break them.
add a comment |
You can use a very simple correlated sub query:
SELECT *, (
SELECT COUNT(DISTINCT game_detail) + 1
FROM game_logs AS x
WHERE user_id = t.user_id AND game_detail > t.game_detail
) AS user_game_rank
FROM game_logs AS t
ORDER BY user_id, user_game_rank
DB Fiddle
It is slower but far more reliable than user variables. All it takes is one JOIN to break them.
add a comment |
You can use a very simple correlated sub query:
SELECT *, (
SELECT COUNT(DISTINCT game_detail) + 1
FROM game_logs AS x
WHERE user_id = t.user_id AND game_detail > t.game_detail
) AS user_game_rank
FROM game_logs AS t
ORDER BY user_id, user_game_rank
DB Fiddle
It is slower but far more reliable than user variables. All it takes is one JOIN to break them.
You can use a very simple correlated sub query:
SELECT *, (
SELECT COUNT(DISTINCT game_detail) + 1
FROM game_logs AS x
WHERE user_id = t.user_id AND game_detail > t.game_detail
) AS user_game_rank
FROM game_logs AS t
ORDER BY user_id, user_game_rank
DB Fiddle
It is slower but far more reliable than user variables. All it takes is one JOIN to break them.
edited Nov 27 '18 at 6:10
answered Nov 27 '18 at 5:58
Salman ASalman A
183k66340438
183k66340438
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53465111%2fdetermine-rank-based-on-multiple-columns-in-mysql%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Is the ordering based on ascending order of
game_idor the descending order ofgame_detail?– Madhur Bhaiya
Nov 25 '18 at 6:15
order of game_detail
– ha͞me̸d̨
Nov 25 '18 at 6:17