简体   繁体   中英

How to query a hotel database to return the query for a single room available for three consecutive nights?

I'm trying to find an answer to the following query: A customer wants a single room for three consecutive nights. Find the first available date in December 2016.

As per the question, this should be the right answer. But I don't know how to solve it.

+-----+------------+
| id  | MIN(i)     |
+-----+------------+
| 201 | 2016-12-11 |
+-----+------------+

The link is from question number 14 here .

This is the ER diagram of the database:数据库的ER图

I apologize that I'm a bit rusty with this kind of query and I can't guarantee that I got all of the syntax correct, but I think that something like the following might work:

SELECT id, DATE_ADD(b.booking_date, INTERVAL (end_date + 1 DAY) as date
FROM  (
         SELECT r.id, STR_TO_DATE('2016-01-01', '%Y-%m-%d') as start_of_month, b.booking_date as start_date, DATE_ADD(b.booking_date, INTERVAL (nights - 1) DAY) as end_date
         FROM room r
         LEFT JOIN booking b ON r.id = b.room_no
         ORDER BY r.id, b.booking_date
      ) as room_bookings
WHERE DATE_DIFF(room_bookings.start_of_month, room_bookings.start_date) >= 3
OR DATE_DIFF(room_bookings.end_date, (
         SELECT b2.booking_date FROM booking b2
         WHERE b2.room_no = room_bookings.id AND b2.booking_date > room_bookings.start_date
         ORDER BY b2.booking_date LIMIT 1)
      ) >= 3

In fact, now that I type that all out, you might be able to tweak the WHERE of the main query so that you don't even need the room_bookings subselect. Hopefully this helps and isn't too far off the mark.

This seems very hard to do without a calendar table -- because an appropriate room might have no booking at all during the month. Without any booking, there is no record in the month to start with.

select r.id, dte
from rooms r cross join
     (select date('2018-12-01') as dte union all
      select date('2018-12-02') as dte union all
      . . .
      select date('2018-12-32') as dte 
     ) d
where not exists (select 1 from bookings b where b.room_no = r.id and b.booking_date = d.dte) and
      not exists (select 1 from bookings b where b.room_no = r.id and b.booking_date = d.dte + interval 1 day) and
      not exists (select 1 from bookings b where b.room_no = r.id and b.booking_date = d.dte + interval 2 day)
order by d.dte
limit 1;

This assumes that booking_date is the start of the stay. You need to provide the logic for a "single room".

select distinct top 1 alll.i,alll.room_no,
case
when (select count(*) from booking where room_no = alll.room_no and booking_date between dateadd(day,1,alll.i) and dateadd(day,3,alll.i)) > 0  then 'Y'
else 'N'
end as av3
 from
(select c.i,b.room_no,b.booking_date
from calendar c cross join booking b
where month(c.i) = 12 and year(c.i) = 2016 and b.room_type_requested = 'single'
) as alll
join
(
select distinct c.i, b.room_no
from calendar c join booking b
on c.i between b.booking_date and DATEADD(day,b.nights-1,b.booking_date)
where month(c.i) = 12 and year(c.i) = 2016 and b.room_type_requested = 'single'
) as booked
on alll.i = booked.i
and alll.room_no <> booked.room_no
order by 1

This works. It is a little complicated but basically first checks all the rooms that are booked and then does a comparison between rooms not booked on each day of the month till the next 3 days.

My solution is separate problem into 2 parts (in the end was 2 queries joined together). May not be the most efficient but the solution is correct.

1) Of the single rooms, look at the last check-out date, and see which one is vacant first (ie no more bookings for the rest of the month) 2) check in between current reservations - and see if there's a 3 day gap between them 3) join those together - grab the min

WITH subquery AS( -- existing single-bed bookings in Dec 
SELECT room_no, booking_date, 
        DATE_ADD(booking_date, INTERVAL (nights-1) DAY) AS last_night
   FROM booking
   WHERE room_type_requested='single' AND
         DATE_ADD(booking_date, INTERVAL (nights-1) DAY)>='2016-12-1' AND 
         booking_date <='2016-12-31'
   ORDER BY room_no, last_night) 

SELECT room_no, MIN(first_avail) AS first_avail --3) join the 2 together
FROM( 

-- 1) check the last date the room is booked in December (available after)

SELECT room_no, MIN(first_avail) AS first_avail
  FROM(
      SELECT room_no, DATE_ADD(MAX(last_night), INTERVAL 1 DAY) AS first_avail
        FROM subquery q3
        GROUP BY 1
        ORDER BY 2) AS t2

UNION 

-- 2) check if any 3-day exist in between reservations 

SELECT room_no, DATE_ADD(MIN(end2), INTERVAL 1 DAY) AS first_avail
  FROM(
      SELECT q1.booking_date AS beg1, q1.room_no, q1.last_night AS end1, 
             q2.booking_date AS beg2, q2.last_night AS end2
        FROM subquery q1
        JOIN subquery q2 
          ON q1.room_no = q2.room_no AND q2.booking_date > q1.last_night
      GROUP BY 2,1
      ORDER BY 2,1) AS t
      WHERE beg2-end1 > 3) AS inner_t

This works conceptually as the first avaiable date should always be the end of the previous booking.

SELECT MIN(DATE_ADD(a.booking_date, INTERVAL nights DAY)) AS i
FROM booking AS a
WHERE DATE_ADD(a.booking_date, INTERVAL nights DAY)
      >= '2016-12-01'
      AND room_type_requested = 'single'
      AND NOT EXISTS
      (SELECT 1 FROM booking AS b
        WHERE b.booking_date BETWEEN
        DATE_ADD(a.booking_date, INTERVAL nights DAY) 
        AND DATE_ADD(a.booking_date, INTERVAL nights+2 DAY)
        AND a.room_no = b.room_no)

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