views:

444

answers:

3

Supposed I have the following tables:

Sailor(sid, sname, age)  
Boat(bid, sid)

Each boat can have many sailors, and each individual sailor can serve on many boats. What I want to do is to find the boat with the highest average age of sailors.

I can find the average age of the sailor on each boat with this subquery:

SELECT b.bid, AVG(s.age) AS avg_age FROM sailor s, boat b
WHERE b.sid = s.sid
GROUP BY b.bid

However, I am stuck on how to find the maximum row from this subquery further on.

P.S. I'm also looking for MySQL-compatible query, if that makes any difference.

+1  A: 
SELECT b.bid, AVG(s.age) AS avg_age 
FROM sailor s JOIN boat b USING (sid)
GROUP BY b.bid
ORDER BY AVG(s.age) DESC
LIMIT 1;

This will return one row, and because of the ordering it will contain the maximum average age.

dnagirl
Unfortunately LIMIT is not available on subqueries.
bobince
MySQL does permit it (perhaps unintentionally) if you "double-subquery" it. E.g. `((SELECT ... LIMIT 1))`
Bill Karwin
I read the "subquery" part of the question as being a red herring. It seemed to me that what he wants is "is to find the boat with the highest average age of sailors". So yes, if the query has to be a subquery, then LIMIT is a problem without the nesting @Bill Karwin suggests.
dnagirl
I donlt understand, where's the subquery ??
Charles Bretana
@Charles Bretana: That was my point. I believe that the questioner thought the answer required a subquery, not that a subquery was actually required.
dnagirl
This query does work and I like the fact that it's a lot more concise than nested query, even though I was looking for a nested-query type of solution.
ejel
+2  A: 
SELECT t1.*
FROM
 (SELECT b.bid, AVG(s.age) AS avg_age FROM sailor s, boat b
  WHERE b.sid = s.sid
  GROUP BY b.bid) t1
LEFT OUTER JOIN
 (SELECT b.bid, AVG(s.age) AS avg_age FROM sailor s, boat b
  WHERE b.sid = s.sid
  GROUP BY b.bid) t2
 ON (t1.avg_age < t2.avg_age)
WHERE t2.avg_age IS NULL;
Bill Karwin
+1 ugly but correct. Doing a separate query first with dnagirl's suggestion of LIMIT may be preferable to a subquery of this complexity if it is not looped. Note that there is no tie-breaking with self-null-joins; if two boats happen to have the same average age two rows will be returned.
bobince
Yes, definitely ugly. I generally don't like to do `GROUP BY` in a subquery. Yuck!
Bill Karwin
This is the solution I was exactly looking for. Even though it is not elegant and @dnagirl's solution might be preferable in real situation, I'd like to understand how SQL subquery works in this case.
ejel
+1  A: 

In sql server it would be:

  Select Top 1 Bid, Avg(age)
  From boat b Join sailor s 
     On s.sid = b.sid
  Group By Bid
  Order By Avg(Age) Desc

I'm not up on MySQL, but there's a Linmit keyword that "limits" the enumber of rows returned, If you use that instead of the Top 1, it should work, no? I'm not exactly sure of the syntax, but ...

  Select  Bid, Avg(age)
  From boat b Join sailor s 
     On s.sid = b.sid
  Group By Bid
  Order By Avg(Age) Desc
   Limit 1
Charles Bretana