Reputation: 3580
The subject of my question is a bit confusing because I'm running out of words to suit the general and technical description of it.
In a less technical terms, what I'm trying to accomplish specifically is:
List all churches and their corresponding booking status from a service they are offering with the following fields:
id
name
pending
status from all services they are offering AS pendingCount
fully booked
status from all services they are offering AS bookedCount
The structure of associate tables are as follows:
+---------+---------------+
| id(int) | name(varchar) |
+---------+---------------+
+---------+---------------+---------------+
| id(int) | name(varchar) | churchId(int) |
+---------+---------------+---------------+
+---------+-----------------+----------------+
| id(int) | status(varchar) | serviceId(int) |
+---------+-----------------+----------------+
What I have come up so far is this, which I'm totally have no idea why it is compiling but produces a negative result:
SELECT
churches.id,
churches.name,
pending.count AS pendingCount,
booked.count AS bookedCount
FROM
churches
INNER JOIN
services
ON
services.churchId = churches.id
LEFT JOIN
(SELECT
COUNT(bookings.id) AS `count`,
bookings.serviceId
FROM
bookings
WHERE
bookings.status = 'pending'
GROUP BY
bookings.serviceId)
AS pending
ON
pending.serviceId = services.id
LEFT JOIN
(SELECT
COUNT(bookings.id) AS `count`,
bookings.serviceId
FROM
bookings
WHERE
bookings.status = 'fully booked'
GROUP BY
bookings.serviceId)
AS booked
ON
booked.serviceId = services.id
Upvotes: 1
Views: 53
Reputation: 16691
I would use conditional aggregation for this. It is a way to sum a number of rows that meet a certain value. In this case, we can use aggregation for SUM(b.status = 'fullyBooked')
and SUM(b.status = 'pending')
and group by church id
like this:
SELECT c.id, c.name,
SUM(b.status = 'pending') AS pendingCount,
SUM(b.status = 'fully booked') AS bookedCount
FROM bookings b
RIGHT JOIN services s ON s.id = b.serviceId
RIGHT JOIN churches c ON c.id = s.churchid
GROUP BY c.id;
To reiterate, by using SUM(condition)
and grouping by a certain id
value, you are counting the number of times that condition is true for that id
value.
EDIT:
I have added a RIGHT OUTER JOIN
to this query so that churches without any bookings will be returned as well.
Upvotes: 4
Reputation: 1987
select c.id, c.name,
SUM(b.status = 'pending') pendingCount,
SUM(b.status = 'fully booked') bookedCount
from church c
-- Left join here if you want all churches whether or not they offer a service
left join services s on (c.id = s.churchId)
-- Left join here if you want all churches w/ a service, but may have no bookings
left join bookings b on (s.id = b.serviceId)
group by c.id, c.name;
Upvotes: 1
Reputation: 158
Try
SELECT
churches.id,
churches.name,
count(case when bookings.status = 'pending' then 1 else null end) AS pendingCount,
count(case when bookings.status = 'fully booked' then 1 else null end) AS bookedCount
FROM
churches
INNER JOIN
services
ON
services.churchId = churches.id
LEFT JOIN bookings on bookings.serviceId=services.id
group by
churches.id,
churches.name,
Upvotes: 0