Possible Duplicate:
SQL ORDER BY total within GROUP BY
UPDATE: I've found my solution, which I've posted here. Thanks to everyone for your help!
I'm developing a Facebook application which requires a leaderboard. Scores and time taken to complete the game are recorded and these are organised by score first, then in the case of two identical scores, the time is used. If a user has played multiple times, their best score is used.
The lower the score, the better the performance in the game.
My table structure is:
id
facebook_id - (Unique Identifier for the user)
name
email
score
time - (time to complete game in seconds)
timestamp - (unix timestamp of entry)
date - (readable format of timestamp)
ip
The query I thought would work is:
SELECT *
FROM entries
ORDER BY score ASC, time ASC
GROUP BY facebook_id
The problem I'm having is in some cases it's pulling in the user's first score in the database, not their highest score. I think this is down to the GROUP BY statement. I would have thought the ORDER BY statement would have fixed this, but apparently not.
For example:
----------------------------------------------------------------------------
| ID | NAME | SCORE | TIME | TIMESTAMP | DATE | IP |
----------------------------------------------------------------------------
| 1 | Joe Bloggs | 65 | 300 | 1234567890 | XXX | XXX |
----------------------------------------------------------------------------
| 2 | Jane Doe | 72 | 280 | 1234567890 | XXX | XXX |
----------------------------------------------------------------------------
| 3 | Joe Bloggs | 55 | 285 | 1234567890 | XXX | XXX |
----------------------------------------------------------------------------
| 4 | Jane Doe | 78 | 320 | 1234567890 | XXX | XXX |
----------------------------------------------------------------------------
When I use the query above, I get the following result:
1. Joe Bloggs - 65 - 300 - (Joes First Entry, not his best entry)
2. Jane Doe - 72 - 280
I would have expected...
1. Joe Bloggs - 55 - 285 - (Joe's best entry)
2. Jane Doe - 72 - 280
It's like the Group By is ignoring the Order - and just overwriting the values.
Using MIN(score) with the group by selects the lowest score, which is correct - however it merges the time from the users first record in the database, so often returns incorrectly.
So, how can I select a user's highest score and the associated time, name, etc and order the results by score, then time?
Thanks in advance!
Your query does not actually make sense, because the order by should be after the group by. What SQL engine are you using? Most would give an error.
I think what you want is more like:
select e.facebookid, minscore, min(e.time) as mintime -- or do you want maxtime?
from entries e join
(select e.facebookid, min(score) as minscore
from entries e
group by facebookid
) esum
on e.facebookid = esum.facebookid and
e.score = e.minscore
group by e.facebookid, minscore
You can also do this with window functions, but that depends on your database.
One approach would be this:
SELECT entries.facebook_id, MIN(entries.score) AS score, MIN(entries.time) AS time
FROM entries
INNER JOIN (
SELECT facebook_id, MIN(score) AS score
FROM entries
GROUP BY facebook_id) highscores
ON highscores.facebook_id = entries.facebook_id
AND entries.score = highscores.score
GROUP BY entries.facebook_id
ORDER BY MIN(entries.score) ASC, MIN(entries.time) ASC
If you need more information from the entries table, you can then use this as a subquery, and join again on the information presented (facebook_id, score, time) to get one row per user.
You need to aggregate twice, is the crux of this; once to find the minimum score for the user, and again to find the minimum time for that user and score. You could reverse the order of the aggregation, but I would expect that this will filter most quickly and thus be most efficient.
You might also want to check which is faster, aggregating the second time: using the minimum score or grouping using the score as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With