views:

69

answers:

8

Hi there!

I've got a little problem with correct query and hope you can help me. My testing tables structures are as follow:

CREATE TABLE IF NOT EXISTS `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`id`),
) ;

CREATE TABLE IF NOT EXISTS `user_throws` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `length` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`,`user_id`),
) ;

The relations between them is many to one (a user can have many throws). The thing is I need to make a somekind ranking page for all users let's say top 100 throwers. So I need a query result to be:

  1. Filtered by users so only the users who have any throws and the length of throw is greater than 0 is correct record.
  2. Filtered by duplicates so only unique users appear on the list (GROUP BY I suppose).
  3. Sorted by length of throws so only the best (greatest) result will be appended.

For that purpose I wrote the query:

SELECT  `user_throws`.`length` , `users` . * 
FROM  `users` 
JOIN  `user_throws` ON (  `user_throws`.`user_id` =  `users`.`id` ) 
WHERE  `user_throws`.`length` >  '0'
GROUP BY  `users`.`id` 
ORDER BY  `user_throws`.`length` DESC 

But the problem is results are not being sorted by longest throw. It filters the throws with length greater than 0 and displays unique users but the length appended does not correspond with desired sorting. I found out that the length value is the value of throw with lowest user_throws.id that is associated with the user. How should the correct query look like?

+1  A: 

Try:

SELECT longthrow,id,name FROM (
SELECT max(t.length) longthrow,u.id,u.name
FROM users u, user_throws t
WHERE u.id=t.user_id
 AND t.length>0
GROUP BY u.id
) ORDER BY longthrow DESC

Which should give you the longest throw of each user, sorted by furthest throw first, shortest last - think that's what you wanted ;)

And I should add before the comments show up, it's better to start it with:

SELECT max(t.length) longthrow,u.id,u.name

Than:

SELECT max(t.length) longthrow,u.*
Rudu
The theory is good but this will not entirely work because you can't select u.* with a MAX aggregate clause unless your also GROUP BY those fields. If you adjust the syntax it should work though.
Aaron D
Actually, and perhaps unfortunately, this executes just fine in MySQL... one of those quirks ;)
Rudu
That's true MAX() will do the greatest result selection but won't order the users by the length of their longest throws. So it's not exactly what I am looking for. Thanks anyway.
Karol Janyst
Easy enough fixed {updated}
Rudu
A: 

How about

SELECT MAX(`user_throws`.`length`) , `users` . * 
...
Jeremy Goodell
The theory is good but this will not entirely work because you can't select users.* with a MAX aggregate clause unless your also GROUP BY those fields. If you adjust the syntax it should work though.
Aaron D
That's true MAX() will do the greatest result selection but won't order the users by the length of their longest throws. So it's not exactly what I am looking for. Thanks anyway.
Karol Janyst
A: 

add a having clause after the group by

SELECT  `user_throws`.`length` , `users` . * 
FROM  `users` 
JOIN  `user_throws` ON (  `user_throws`.`user_id` =  `users`.`id` ) 
GROUP BY  `users`.`id` 
HAVING `user_throws`.`length` > 0
ORDER BY  `user_throws`.`length` DESC 

EDIT: I see now that this isn't what you're looking for. You're looking for the best throw from each user. Some other answers are more applicable.

JungleFreak
The having clause just repeats the where clause and does not produce the longest throw. You need to use the MAX aggregate function.
Aaron D
A: 

You could try a subquery solution that looks something like this:

   SELECT  
      (SELECT MAX(length) FROM user_throws WHERE user_id=users.id) AS LongThrow,
      users.* 
   FROM  users
   WHERE (SELECT MAX(length) FROM user_throws WHERE user_id=users.id) >  '0'
   ORDER BY (SELECT MAX(length) FROM user_throws WHERE user_id=users.id) 

Note: The syntax is off the top of my head... Please take with a grain of salt. Also, the query above is highly verbose but should work out of the gate. You can abbreviate it as you see fit.

But in any case, the subquery idea will work. You might just have to tweak/play around with the syntax.

Paul Sasik
I'd like to avoid subqueries as much as I can. So doing that operation in one query is desired solution (I think it's possible).
Karol Janyst
@Karol: Subqueries can be your friend. They can work very well if your table indexing is done right and the query optimization decent. But they can also hurt if abused. The only possible trouble i see with my example is if your user_throws.user_id fkey isn't indexed. In any case, i would try it. Should just take a minute.
Paul Sasik
A: 

try

SELECT  u2.`length` , u . * 
FROM  `users` u ,  `user_throws` u2 where u2.`user_id` =  u.`id` 
AND  u2.`length` >  '0' and u2.`id` = (select u1.id from `user_throws` u1 where u1.`user_id` =  u.id  order by u1.`length` desc limit 1)
GROUP BY  u2.`user_id` 
Yogesh
A: 

SELECT user_throws2.length , users . * FROM users JOIN (SELECT * FROM user_throws ORDER BY length DESC ) as user_throws2 ON ( user_throws2.user_id = users.id ) WHERE user_throws2.length > '0' GROUP BY users.id ORDER BY user_throws2.length DESC

try it, works on last mysql (if not work in you mysql update that :D)

dexah
A: 

A slightly different approach:

select ut.longest, u.id, u.name
from users u
join
(select user_id, max(length) longest
 from user_throws
 where length > 0
 group by user_id) ut on u.id = ut.user_id
order by ut.longest desc
Mark Bannister
A: 

SELECT MAX(user_throws.length) , users . * FROM users JOIN user_throws ON ( user_throws.user_id = users.id ) WHERE user_throws.length > '0' GROUP BY users.id ORDER BY user_throws.length DESC

i try that, and it works, just check execution time for the two options

dexah