0

I would like to produce a list like this for a specific event where the number of volunteers currently signed up are below the quantity needed. I am using MySQL:

Volunteer Role
==============
Stewarding
Video

(First aid does not appear as there are already 2 people signed up to this team)

The tables I have are:

Delegate
========
OrderId   Name      volunteerOption
1         Tim       
1         Jane      First Aid
1         Mary      First Aid
1         Jo        Stearding


VolunteerRole
===========
eventId   volunteerName   quantityNeeded
1         First Aid       2
1         Stewarding      10


Orders
======
Id        eventId
1         1

Event
=====
id      name
1       Fun Run

I think I need to use subqueries with a join but I'm not sure how to link them.

The two queries I have are:

SELECT roleName, quantity as size
FROM VolunteerRole
WHERE eventId = 1

Which gives me the team names and max size

SELECT DISTINCT count(volunteerOption) as volunteers, volunteerOption
FROM delegate
JOIN orders on delegate.orderId = orders.id
WHERE orders.eventId = 1 and volunteerOption <> '' 
GROUP BY volunteerOption

which gives me the current quantity of volunteers on each team. The bit I can't figure out is how to select only the teams with number of volunteers less than maximum.

Any help gratefully recieved

4
  • Is Tim meant to have a volunteerOption value of Video? Commented Apr 27, 2017 at 11:10
  • Can each Event have many Order's? Commented Apr 27, 2017 at 11:18
  • No need to do SELECT DISTINCT since the GROUP BY returns no duplicates. Commented Apr 27, 2017 at 11:25
  • Your sample data does not include a record in VolunteerRole for Video. And you have misspelt Stewarding in Delegate. Commented Apr 27, 2017 at 11:33

4 Answers 4

1

Please try the following...

SELECT volunteerName AS 'Volunteer Role'
FROM ( SELECT volunteerName AS volunteerName,
              quantityNeeded AS quantityNeeded,
              COUNT( volunteerOption ) AS volunteersCount
       FROM Delegate
       JOIN Orders ON Delegate.OrderId = Orders.Id
       RIGHT JOIN VolunteerRole ON Orders.eventId = VolunteerRole.eventId
                               AND Delegate.volunteerOption = VolunteerRole.volunteerName
       WHERE Orders.eventId = 1
       GROUP BY volunteerName,
                quantityNeeded
     ) AS volunteersCountFinder
WHERE quantityNeeded > volunteersCount
GROUP BY volunteerName;

This statement starts by performing an INNER JOIN between Delegate and Orders, giving us a list of delegates assigned to each order and thus to each event.

This list is then right-joined to VolunteerRole, giving us a list of delegates assigned to each event and each role within that event. A RIGHT JOIN is performed rather than an INNER JOIN so that roles at an event are still listed even when there are no delegates assigned.

Please note that a RIGHT JOIN is much the same as a LEFT JOIN. Which you use is determined by which side of the JOIN has the table from where nonmatching records should be retained.

The dataset resulting from the two joins is then refined to just those records with an eventId of 1 via the WHERE clause.

The refined dataset is then grouped by volunteerName. Subgrouping by quantityNeeded does not effectively refine or broaden the grouping by volunteerName since each value of volunteerName will have only one corresponding value of quantityNeeded, but GROUP BY requires you to use all fields not generated by an aggregate function for grouping.

The count of each non-NULL value of volunteerOption is then calculated. COUNT() will return 0 where a role has no delegates assigned, i.e. where it encounters a NULL value rather a non-NULL value for volunteerOption (not to be confused with volunteerName here, which will always have a value in each record).

The subquery then returns a list consisting of each volunteerName and its corresponding quantityNeeded and volunteersCount (the alias given to our count).

The main query then refines the subquery's dataset to just those where the quantity needed is greater than the count of assigned volunteers and group's the results by volunteerName. Each value of volunteerName from this group is then returned by the statement.

If you have any questions or comments, then please feel free to post a Comment accordingly.

Appendix

If you wish to extend this statement to list all events that have inadequately filled roles along with each of those inadequately filled roles, you can use...

SELECT eventId,
       volunteerName AS 'Volunteer Role'
FROM ( SELECT Orders.eventId AS eventId,
              volunteerName AS volunteerName,
              quantityNeeded AS quantityNeeded,
              COUNT( volunteerOption ) AS volunteersCount
       FROM Delegate
       JOIN Orders ON Delegate.OrderId = Orders.Id
       RIGHT JOIN VolunteerRole ON Orders.eventId = VolunteerRole.eventId
                               AND Delegate.volunteerOption = VolunteerRole.volunteerName
       GROUP BY Orders.eventId,
                volunteerName,
                quantityNeeded
     ) AS volunteersCountFinder
WHERE quantityNeeded > volunteersCount
GROUP BY eventId,
         volunteerName;
Sign up to request clarification or add additional context in comments.

Comments

0

This gives you event_id,order_id,employee_type,employee_needed wherever you need some:

SELECT a.id as event_id, b.id as order_id, d.volunteerOption, c.quantityNeeded-d.employeed as still_to_find
FROM events a
LEFT JOIN orders b ON a.id=b.eventId
LEFT JOIN VolunteerRole c ON a.id=c.eventId
LEFT JOIN (SELECT order_id, volunteerOption, COUNT(*) as employeed FROM Delegate WHERE volunteerOption IS NOT NULL GROUP BY order_id, volunteerOption) d ON b.id=d.order_id AND c.volunteerName=d.volunteerOption
WHERE c.quantityNeeded<d.employeed 

Comments

0

I think you will have to do that in two steps, because you can't check an aggregated value against a non-aggregated one

select  distinct volunteerName
from    (
            SELECT  t1.volunteerName, t1.quantityNeeded, count(t2.Name) as cnt
            FROM    VolunteerRole t1
            JOIN    Orders t2
            ON      t1.eventId = t2.eventId
            LEFT JOIN
                    Delegate t3
            ON      t1.VolunteerName = t3.volunteerOption and
                    t2.Id = t3.OrderId
            WHERE   t1.eventId = 1
        ) t1
where   quantityNeeded > cnt

Comments

0

Try this query. this should work fine

select  distinct volunteerName from (SELECT  t1.volunteerName, t1.quantityNeeded, count(t2.Name) as cnt FROM    VolunteerRole t1 JOIN    Orders t2 ON      t1.eventId = t2.eventId
LEFT JOIN Delegate t3 ON t1.VolunteerName = t3.volunteerOption and t2.Id = t3.OrderId WHERE   t1.eventId = 1
 ) t1 where   quantityNeeded > cnt;

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.