views:

57

answers:

3

I have a table with 4 columns: place_id, username, counter, last_checkin

I'm writing a check-in based system and I'm trying to get a query that will give me the "mayor" of each place. The mayor is the one with most check-ins, and if there is more than 1 than the the minimum last_checkin wins.

For example, if I have:

place_id, username, counter, last_checkin
 123,     tom,       3 ,      13/4/10
 123,     jill,      3,       14/4/10
 365,     bob,       2,       15/4/10
 365,     alice,     1,       13/4/10

I want the result to be:

123, tom
365, bob

I'm using it in PHP code


Here is the test data:

CREATE TABLE `my_table` ( `place_id` int(11), `username` varchar(50), `counter` int(11), `last_checkin` date);
INSERT INTO `my_table` VALUES (123,'tom',3,'2010-04-13'),(123,'jill',3,'2010-04-14'),(365,'bob',2,'2010-04-15'),(365,'alice',1,'2010-04-13');
A: 
$data = query("SELECT max(counter) counter,username FROM table GROUP By place_id ORDER By last_checkin DESC");
Māris Kiseļovs
Why the max(place_id)? Because of max(counter)?
fabrik
a little correction$data = query("SELECT place_id,username FROM table GROUP By MAX(counter) ORDER By last_checkin DESC");
Flakron Bytyqi
sorry, i did a mistake and i fixed it. Happens :)
Māris Kiseļovs
@Māris, Your query does not satisfy the required criteria. It will select max of the counter and then it will randomly pick the username from the group, so from sample data it would select tom or jill for place_id 123. From http://dev.mysql.com/doc/refman/5.0/en/group-by-hidden-columns.html - 'The server is free to return any value from the group, so the results are indeterminate unless all values are the same.' Also, the query does not need to return the max(counter), but it needs to return place_id.
Unreason
+2  A: 

How about..

SELECT
    place_id,
    (SELECT username
         FROM my_table MT2
         WHERE MT2.place_id = MT1.place_id
         ORDER BY counter DESC, last_checkin ASC
         LIMIT 1) AS mayor
    FROM my_table MT1
    GROUP BY place_id;

Edited as Unreason suggests to have ascending order for last_checkin.

Brian Hooper
last_checking should be ascending, quote: 'if there is more than 1 than the the minimum last_checkin wins'
Unreason
Oops. You're quite right. I'll edit and correct.
Brian Hooper
OMG that worked! (with Unreason's fix)Thank You!
joe
+2  A: 

Brian's correlated query is something I would write. However I found this different take and it might perform differently depending on the data

SELECT
    mt1.place_id,
    mt1.username
FROM 
    my_table mt1 LEFT JOIN my_table mt2 
        ON mt1.place_id = mt2.place_id AND
           (mt1.counter < mt2.counter OR 
            (mt1.counter = mt2.counter AND mt1.last_checkin > mt2.last_checkin)
           )
WHERE 
    mt2.place_id IS NULL

Which uses left join to get to the top records according to certain conditions.

Unreason
I've always been a subquerian, as I find joins make my head hurt. But your solution is also correct, and I suspect joinists are in the majority on this site. I don't think there'll be any performance difference as the query optimiser can rewrite subqueries as joins if it thinks proper. Do whatever you find easiest.
Brian Hooper
Actually I believe that in this case subquery would perform faster on some 'normal' dataset and that mysql might get confused on the complicated join and that it would not know how to rewrite it as subquery, nor that it would use indexes efficiently. However, I liked the principle (was fresh to me) and I am neither subquerian nor joinists, but pro choice :) so I offered a different solution.
Unreason