I have a very interesting challenge that I’m either looking too hard at (and missing a simple solution), or it is more complex than I can figure out heh
Essentially, I have a game that tracks scores. The table named scores
has the columns:
id
(pk, ai), email
, name
, duration
, moves
A player (identified by email) can have multiple rows. To determine the winners, we apply bubble sorting on duration then moves. The challenge is this: How do we get a unique list (by email) of winners?
GROUP BY
doesn’t work as expected because this is an aggregate. We need to keep parity between duration and moves.
Here’s an example of the schema and problem: http://sqlfiddle.com/#!9/0b1530/2 — notice how the same emails show up as expected.
This example uses GROUP BY, but it selects the wrong data: http://sqlfiddle.com/#!9/0b1530/3 — notice how baz@biz.com’s score is incorrect. The correct score should be 30
, 31
(duration & moves, respectively). cow@guy.com should show 31
, 35
(duration & moves, respectively)
And then lastly, selecting distinct on email column won’t work here either, example: http://sqlfiddle.com/#!9/0b1530/4
I’ve tried a few JOIN’s, but I cannot get those to work and also keep row parity either.
UPDATE #1
- The highest score is the amalgamation of first the lowest
duration
and then the lowestmoves
count - The expected results from my example are:
email name duration moves foo@bar.com foo bar 20 36 baz@biz.com baz biz 30 31 cow@guy.com cow guy 31 35
UPDATE #2
Due to JSFiddle limitations, I’ve moved everything over to https://www.db-fiddle.com/f/vV7XMKkoFaynYEajLtYm9h/2
Please note that some of the queries in your answers do not work. I’ve used real data here (except changed emails/names) so that answers can test real data.
Here are the expected results from this sample data:
email duration moves dp@dp.com 32 22 bw@bw.com 33 21 m@m.com 38 23 s@s.com 40 25 foo@bar.com 41 22
Advertisement
Answer
One way to do this is using two nested IN
expressions. The inner expression finds all combinations of email and duration that have the lowest duration for that email. The outer expression then finds the email, duration and moves values that are the lowest moves value for that duration (which is the lowest for that email).
SELECT * FROM scores WHERE (email, duration, moves) IN ( SELECT email, duration, MIN(moves) FROM scores WHERE (email, duration) IN ( SELECT email, MIN(duration) FROM scores GROUP BY email) GROUP BY email, duration) ORDER BY duration, moves
Output:
id email name duration moves submitted 219 foo@bar.com foo bar 20 36 2019-12-09T20:36:19Z 224 baz@biz.com baz biz 30 31 2019-12-09T20:36:19Z 233 cow@guy.com cow guy 31 35 2019-12-09T20:36:19Z
If you don’t care about the submitted time, you can remove the outer nested expression:
SELECT email, name, duration, MIN(moves) AS moves FROM scores WHERE (email, duration) IN ( SELECT email, MIN(duration) FROM scores GROUP BY email) GROUP BY email, name, duration ORDER BY duration, moves
Output:
email name duration moves foo@bar.com foo bar 20 36 baz@biz.com baz biz 30 31 cow@guy.com cow guy 31 35