简体   繁体   中英

SQL reservation hotel room

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 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?

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')
   );

demo here

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;

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)

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 

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM