Reputation: 2177
I want to create a reservation hotel's room using PHP, and to see any available rooms, I use query like this:
SELECT * FROM m_room WHERE room_id NOT IN
(SELECT order_room FROM m_order WHERE
'$start' >= order_bookstart AND '$end' < order_bookend)
ORDER BY room_name
$start
and $end
is input from a textbox. User first fill the start input and end input, after click search, it will show any available rooms. From my query, If my example data that exist in m_order is: room_id->1; start-> 2015-05-13; end-> 2015-05-16
then when I fill $start
with : 2015-05-14 and $end
with : 2015-05-15, it works normally, room_id no. 1 won't shows up, but if I fill the date $start
with : 2015-05-14 and $end
with: 2015-05-19, room_id no. 1 still shows up, which must be not because still used by a client from 2015-05-13 till 2015-05-16
So, how to fix my query to show any available rooms?
Upvotes: 1
Views: 1336
Reputation: 33935
Consider the following
1 2 3 4 5 6 7 8 9
A |---|
B |-----|
C |----|
D |-----------|
The test for overlaps is simpler than pala and others seem to appreciate.
Event B overlaps event A because event B starts before event A ends, and ends after event A starts.
Event C does NOT overlap event B because although event C ends after event B starts, it starts after event B ends
Event D overlaps all the other events because event D starts before all of the other events end, and ends after all the other events start.
So the test for overlaps is simply:
y_start < x_end AND y_end > x_start
Upvotes: 0
Reputation: 9010
I think you might be taking the wrong approach. You want to find rooms which are available for the whole period, which means if there is ANY booking that has either a start, or an end date, that encompasses either the start or end date of the period you are looking for, it must be rejected.
We can use where not exists
to accomplish this. For example, for checking if there is a room available from the 14th to the 15th:
select *
from m_room
where not exists (
select 1
from order_room
where room_id = m_room.room_id
and ('2015-05-14' between start_date and end_date
or '2015-05-15' between start_date and end_date
or start_date between '2015-05-14' and '2015-05-15')
);
As a second option, you could also left join
to the order_room
table, putting the date range in the join criteria, and then checking for rows where the order_room
values are null
.
select *
from m_room
left join order_room
on m_room.room_id = order_room.room_id
and ('2015-05-14' between start_date and end_date
or '2015-05-15' between start_date and end_date
or start_date between '2015-05-14' and '2015-05-15')
where start_date is null;
this demo has the left join
option as well, showing the results are equivalent. This method has the advantage that you get two meaningful results - it shows you the rooms that are definitely available, and if you remove the where
clause, it will also show you the booking times that conflict for the other rooms.
If you want to allow check-in/check-out on the same day that overlaps another bookings check-out/check-in days, we just need to modify the date range by one day, like so:
select *
from m_room
where not exists (
select 1
from order_room
where room_id = m_room.room_id
and ('2015-05-16' between start_date and end_date - interval 1 day
or '2015-05-20' between start_date - interval 1 day and end_date
or start_date between '2015-05-16' and '2015-05-20')
);
OR
select *
from m_room
left join order_room
on m_room.room_id = order_room.room_id
and ('2015-05-16' between start_date and end_date - interval 1 day
or '2015-05-20' between start_date - interval 1 day and end_date
or start_date between '2015-05-16' and '2015-05-20')
where start_date is null;
Upvotes: 3
Reputation: 894
Room ID 1 will show up with the second query because the end date entered is after the order_bookend, so I'd imagine you will need to check the start date entered us between. A booking start and end, or the entered end date is between a booking start and end
WHERE ('$start'>= order_bookstart OR '$start'< order_bookend) OR
('$end'>= order_bookstart OR '$end'< order_bookend)
Upvotes: 0
Reputation: 1637
In order for the room to not be included in the search results, the WHERE condition should cover the following cases:
a) bookstart----start--------bookend-------end------------
b) -------------start--------bookstart-----end-----bookend
c) -------------start--bookstart--bookend--end------------
d) bookstart----start----------------------end-----bookend
In other words, either bookstart or bookend is between start and end (cases a - c), or bookstart is before start and bookend is after end (case d):
So the correct SQL should look like this:
WHERE (order_bookstart >= $start AND order_bookstart < $end) OR
(order_bookend >= $start AND order_bookend < $end) OR
(order_bookstart < $start AND order_bookend > $end)
Upvotes: -1