问题
I've got a 'user_score' table with that structure:
|id|user_id|group_id|score| timestamp |
| 1| 1| 1| 500| 2013-02-24 18:00:00|
| 2| 2| 1| 200| 2013-02-24 18:01:50|
| 3| 1| 2| 100| 2013-02-24 18:06:00|
| 4| 1| 1| 6000| 2013-02-24 18:07:30|
What I need to do is to select all users from that table which are from the exact group. Select their actual (according to timestamp) score in that group and their rank.
What I have is (edit: after Jocachin's comment I found out that my own query does not work as I expected, sorry to all):
SELECT user_id, score, @curRank := @curRank + 1 AS rank
FROM (
SELECT *
FROM (
SELECT * FROM `user_score`
WHERE `group_id` = 1
ORDER BY `timestamp` DESC
) AS sub2
GROUP BY `user_id`
) AS sub, (SELECT @curRank := 0) r
ORDER BY `rank`
Expected result for example data and group_id = 1:
|user_id|score|rank|
| 1| 6000| 1|
| 2| 200| 2|
But MySQL subselects are a bit problematic, do you see any other solution, please?
I'll probably need to get the rank od single user in the group later. I am lost at the moment.
回答1:
Although I'm not sure what "problematic" means in this context, here is the query rewritten as a plain LEFT JOIN
with a subquery just to get the ranking right at the end (the ORDER BY
needs to be done before the ranking);
SELECT user_id, score, @rank := @rank + 1 AS rank FROM
(
SELECT u.user_id, u.score
FROM user_score u
LEFT JOIN user_score u2
ON u.user_id=u2.user_id
AND u.`timestamp` < u2.`timestamp`
WHERE u2.`timestamp` IS NULL
ORDER BY u.score DESC
) zz, (SELECT @rank := 0) z;
An SQLfiddle to test with.
EDIT: To take group_id into account, you'll need to extend the query somewhat;
SELECT user_id, score, @rank := @rank + 1 AS rank FROM
(
SELECT u.user_id, u.score
FROM user_score u
LEFT JOIN user_score u2
ON u.user_id=u2.user_id
AND u.group_id = u2.group_id -- u and u2 have the same group
AND u.`timestamp` < u2.`timestamp`
WHERE u2.`timestamp` IS NULL
AND u.group_id = 1 -- ...and that group is group 1
ORDER BY u.score DESC
) zz, (SELECT @rank := 0) z;
Another SQLfiddle.
回答2:
SELECT user_id, score, @rank := @rank + 1 AS rank
FROM
(
SELECT
user_id AS user_id,
SUBSTRING_INDEX(GROUP_CONCAT(score ORDER BY timestamp DESC), ',', 1)
AS score
FROM user_score
WHERE group_id=1
GROUP BY user_id
) AS u,
(
SELECT @rank := 0
) AS r
ORDER BY score DESC;
回答3:
I know an answer has been posted and accepted but I have a point about your original query that I think is worth mentioning.
When you group by, you are only allowed to select the columns you group by and those with aggregate functions such as MAX and COUNT. Selecting other columns is technically wrong.
This makes sense since it is not clear the data of which row should be returned. Mysql returns the data of the first row in group by; that is why your query works (because of the order by timestamp in the inner sub query). SQL server on the other hand raises an exception.
I think SQL Server approach is correct and this type of query should be avoided.
回答4:
I ran into a similar problem today and after unsuccessfully trying to implement the accepted answer I did this much simpler query
SELECT user_score.user_id,
MAX(user_score.score) AS score
FROM user_score
WHERE user_score.group_id = 1
GROUP BY user_score.user_id,
user_score.group_id
ORDER BY user_score.score DESC,
user_score.`timestamp` ASC
来源:https://stackoverflow.com/questions/15054456/mysql-select-rank-for-users-in-a-score-table