1

Basically below is how my table in sql looks like, I need to find the available seat number in each room. When the time leave is null it means there still people in it if there is a time, which mean the person has already leave.

'Room' Table

roomNo seatNum
1 30
2 20
3 10
4 25
5 10

'People' Table

ID RoomNo TimeLeave
101 1 15:47:20
102 5 NULL
103 4 15:40:20
104 2 NULL
105 2 NULL

So How can i make the query output to something like below

RoomNo AvailableSeatNum NotAvailable
1 30 0
2 18 2
3 10 0
4 25 0
5 9 1

I tried doing sql like

SELECT room.roomNo,room.seatNum-count(people.id), count(people.id)
FROM room,people
WHERE room.roomNo=people.roomNo AND Timeleave is NULL
GROUP BY room.roomNo

But the results is only those room which are OCCUPIED ONLY

RoomNo AvailableSeatNum NotAvailable
2 18 2
5 9 1
Dream
  • 19
  • 5
  • not really sure what you mean, Im still new to sql, can you explain more about it? – Dream Mar 22 '21 at 08:14
  • 1
    Tip of today: Switch to modern, explicit `JOIN` syntax. Easier to write (without errors), easier to read (and maintain), and easier to convert to outer join if needed. – jarlh Mar 22 '21 at 08:28
  • This is a strange data model. People can only ever go into one room? – Gordon Linoff Mar 22 '21 at 11:43

3 Answers3

4

Use a LEFT JOIN instead of what you have now (an implicit INNER JOIN).

As @FaNo_FN pointed out, the 2nd and 3rd column of your desired result are a bit trickier than I first thought. You want to count NULL TimeLeave rows in each group - COUNT(*) - COUNT(people.TimeLeave) - but only for all the rows that are part of the join (where, for example, people.roomNo is not NULL).

Something like this:

SELECT
    room.roomNo,
    room.seatNum - IF(people.roomNo IS NULL, 0, COUNT(*) - COUNT(people.TimeLeave)),
    IF(people.roomNo IS NULL, 0, COUNT(*) - COUNT(people.TimeLeave))
FROM room
LEFT JOIN people
ON room.roomNo = people.roomNo
GROUP BY room.roomNo, room.seatNum

Feel free to study up on different types of JOINs: What's the difference between INNER JOIN, LEFT JOIN, RIGHT JOIN and FULL JOIN?

Janez Kuhar
  • 3,705
  • 4
  • 22
  • 45
  • WHERE is considered as a INNER JOIN ? – Dream Mar 22 '21 at 08:43
  • 1
    Well, `WHERE` itself is just a keyword. See [Explicit vs implicit SQL joins](https://stackoverflow.com/q/44917/6367213) for more info. – Janez Kuhar Mar 22 '21 at 08:45
  • @Dream your "implicit inner join" is this part `FROM room,people`. By specifying two table objects to `SELECT FROM`, you are implying you want them joined. – ed2 Mar 22 '21 at 09:38
0

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:

  1. 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 ...
  2. 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.
  3. 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

FanoFN
  • 6,815
  • 2
  • 13
  • 33
-1

It's fine , desired result u wish

select Peoples.RoomNo, Room.seatNum - COUNT(Peoples.RoomNo), COUNT(Peoples.RoomNo) as NotAvailable from Room inner join Peoples on Room.roomNo = Peoples.RoomNo 
where Peoples.TimeLeave is null
group by Peoples.RoomNo, Room.seatNum
union all
select Room.RoomNo, Room.seatNum, 0 from Room where Room.roomNo not in (select Peoples.RoomNo from Peoples)
union all
select Peoples.RoomNo, Room.seatNum, 0 from Room inner join Peoples on Room.roomNo = Peoples.RoomNo 
where Peoples.TimeLeave is not null
group by Peoples.RoomNo, Room.seatNum
Khoa Hoàng
  • 84
  • 1
  • 7