Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQL group & sort by two columns [duplicate]

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!

like image 981
Darren Craig Avatar asked Mar 03 '26 18:03

Darren Craig


2 Answers

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.

like image 61
Gordon Linoff Avatar answered Mar 06 '26 06:03

Gordon Linoff


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.

like image 35
penguat Avatar answered Mar 06 '26 07:03

penguat