views:

631

answers:

6
+1  Q: 

SQL Subquery

I have SQL data that looks like this

events
id name         capacity
1  Cooking        10
2  Swimming       20
3  Archery        15

registrants
id  name
1   Jimmy
2   Billy
3   Sally

registrant_event
registrant_id  event_id
     1             3
     2             3
     3             2

I would like to select all of the fields in 'events' as well as an additional field that is the number of people who are currently registered for that event. In this case Archery would have 2 registrants, Swimming would have 1, and Cooking would have 0.

I imagine this could be accomplished in a single query but I'm not sure of the correct syntax. How would a query be written to get that data?

Update: Thanks for the great answers, you all rock!

+6  A: 
SELECT e.*, ISNULL(ec.TotalRegistrants, 0) FROM events e LEFT OUTER JOIN
(
   SELECT event_id, Count(registrant_id) AS TotalRegistrants
   FROM registrant_event
   GROUP BY event_id
) ec ON e.id  = ec.event_id
Greg Dean
I'm assuming you mean e.id instead of e.event_id in the last line.
R. Bemrose
This is being used in a mysql environment so I changed ISNULL() to IFNULL() and it worked great. Thanks!http://dev.mysql.com/doc/refman/5.0/en/control-flow-functions.html
GloryFish
+2  A: 
SELECT Events.ID, Events.Name, Events.Capacity, 
       ISNULL(COUNT(Registrant_Event.Registrant_ID), 0)
FROM Events
LEFT OUTER JOIN Registrant_Event ON Events.ID = Registrant_Event.Event_ID
GROUP BY Events.ID, Events.Name, Events.Capacity
devio
GROUP BY Events.ID, Events.Name, Events.Capacity is not correct
Greg Dean
Why isn't that correct?
Mitchel Sellers
The proper way to use GROUP BY in this case is with a sub query. Putting all the selected fields in GROUP BY is a common mistake.
Greg Dean
1) there's a difference between "not correct" as in "won't work" and "not good style".2) as no database is mentioned in the question, we do not know whether the database in case supports subqueries.
devio
subquery is one of the article's tags.
R. Bemrose
It's also the question's title
Greg Dean
Subquery is also an inappropriate solution. This is a case where people jumped to answer the question without considering the need.
Pittsburgh DBA
Considering the question specifically asks for a sub query, I don't think that is possible.
Greg Dean
A: 
select e.id, e.name, e.capacity, IsNull(re.eventCount,0) from events e
left join (
 select count(event_id) as eventCount, event_id  from registrant_event group by event_id
 ) re 
on e.id = re.event_id
Kevin
I think you mean count(registrant_id)
Greg Dean
it also works with count(event_id)
Eduardo Campañó
I never thought if doing it like that, but I see that it will work. Any advantages?
Greg Dean
Honestly, I don't know if there are any advantages of doing it that way but I doubt it. I just did it that way since that is what we are grouping by.
Kevin
A: 
SELECT
  events.*
, COUNT(registrant_event.registrant_id) AS registrantsCount
FROM events
LEFT JOIN registrant_event ON events.id = registrant_event.event_id
GROUP BY events.id
Jacco
that will fail if there are fields in the events table not in the group by clause. IE: anything other than id
Joel Coehoorn
It is correct in MySQL. I'm not sure if it works in MsSQL
Jacco
This query croaks on ANSI-compliant SQL servers... including MySQL in ANSI mode (started with the --ansi switch).
R. Bemrose
More MySQL unholiness.
Pittsburgh DBA
A: 
SELECT e.id, count(*) AS NumRegistrants
FROM events e
LEFT JOIN registrant_event re ON re.event_id=e.id
GROUP BY e.id

Note that this will return 1 instead of 0 for events with no registrants. To get it to show 0 instead, you have to get a little more complicated:

SELECT *,
    (SELECT COUNT(*) FROM registrant_event WHERE event_id=id) AS NumRegistrants
FROM events
Joel Coehoorn
using count on a column/field that contains nulls will not count them... such as COUNT(re.registrant_id)
R. Bemrose
Indeed. COUNT(*) is the source of many errors in these cases.
Pittsburgh DBA
A: 

I tested this in Oracle 11g, and it seems to work

SELECT e.id, e.name, e.capacity, COUNT(re.event_id)
FROM events e
LEFT JOIN registrant_event re
  ON e.id = re.event_id
GROUP BY e.id, e.name, e.capacity
R. Bemrose