I hardly find any reason to use the old comma-join and you might never achieve your desired result if you stick to it. Try this query:
SELECT r.roomno,
MAX(r.seatNum)-SUM(CASE WHEN p.roomno IS NULL THEN 0 WHEN p.roomno IS NOT NULL AND p.TimeLeave IS NOT NULL THEN 0 ELSE 1 END) AS AvailableSeatNum,
SUM(CASE WHEN p.roomno IS NULL THEN 0 WHEN p.roomno IS NOT NULL AND p.TimeLeave IS NOT NULL THEN 0 ELSE 1 END) AS NotAvailable
FROM room AS r
LEFT JOIN people AS p
ON r.roomNo=p.roomNo
GROUP BY r.roomno;
My mind is actually not in excellent shape right now so this is what I can come up. I'll attempt to break it down and explain to you one by one but I may miss a thing or two to mention. Anyways:
- Change from comma-join to use
JOIN
; and in this particular problem, use LEFT JOIN
. I'm talking about your query part here .. FROM room,people ..
.
- Assign aliases to your table
.. room AS r ...
. These are just for readability purposes and especially useful for long table names. Once you've assign and alias, you can use it throughout the query. Just don't use duplicate aliases.
- According to your example data, it seems like the value for
seatNum
is fixed so I used MAX()
to cater sql_mode=only_full_group_by. There are other methods too like ANY_VALUE() but I'll just use MAX()
for this example. However, I assume that you're sql_mode=only_full_group_by
is turned off but if it's not, consider these methods.
In the SELECT
, I've made a bunch of CASE
expression condition to get the calculation according to your desired result. I attempt to make it work without using sub-query but I personally would prefer to use a sub-query at least; it's easier for me to read. What I mean is:
SELECT rp.roomno,
MAX(rp.seatnum)-SUM(rp.cval) AS AvailableSeatNum,
SUM(rp.cval) AS NotAvailable
FROM
(SELECT r.roomno, r.seatNum, p.id, p.TimeLeave,
CASE WHEN p.roomno IS NULL THEN 0
WHEN p.roomno IS NOT NULL AND p.TimeLeave IS NOT NULL THEN 0
ELSE 1 END AS cval
FROM room AS r
LEFT JOIN people AS p
ON r.roomNo=p.roomNo) rp
GROUP BY rp.roomno;
With a sub-query, I only need to do CASE
expression once and then later just refer to it on the outer query. Let's focus on the CASE
:
CASE WHEN p.id IS NULL THEN 0
WHEN p.id IS NOT NULL AND p.TimeLeave IS NOT NULL THEN 0
ELSE 1 END AS cval
WHEN p.roomno IS NULL THEN 0
is checking the result of LEFT JOIN
between room
and people
table. Since, people
table don't have roomno=3
, it will return p.roomno
as NULL
, hence CASE
will change that to 0.
WHEN p.roomno IS NOT NULL AND p.TimeLeave IS NOT NULL THEN 0
checks if the LEFT JOIN
have matches of roomno
for both table, it will return value for p.roomno
and if p.TimeLeave
have value instead of NULL
, means it's occupied.
and ELSE 1
is just telling that if any of the condition above is not met, return 1. With the values returned between 0
and 1
, you'll just have to do SUM()
instead of COUNT()
at the outer query (since COUNT()
will count how many rows).
I hope the explanation is easy to understand. Here is a fiddle for reference:
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=17b88d7de91f4135f6bc1a795ff2a86e