2016-03-06 112 views
1

我使用查詢從hotel DB查找免費房間。我寫了選擇的房間是不是在booking表的查詢:通過SQL查詢在酒店中查找免費房間

SELECT * FROM room WHERE roomId NOT IN 
    (SELECT roomId FROM booking b WHERE STR_TO_DATE('${endDate}', '%m-%d-%Y') <= b.endDate AND 
    STR_TO_DATE('${startDate}', '%m-%d-%Y') >= b.startDate); 

booking表如下所示:

+-----------+------------+------------+--------+---------+ 
| bookingId | startDate | endDate | roomId | guestId | 
+-----------+------------+------------+--------+---------+ 
|   1 | 2016-03-12 | 2016-03-22 |  1 |  1 | 
+-----------+------------+------------+--------+---------+ 

但如果我的startDate2016-03-10endDate2016-03-25 - 我有已經預訂房間從2016-03-122016-03-22。我如何解決它?我不需要顯示在我的日期之間預訂的房間。

+1

如果事件A在事件B結束之前開始並且在事件B開始之後結束,則事件A與事件B重疊。 – Strawberry

+0

@Strawberry像這樣,其中'3-8-2016'選擇開始和'3-15-2016'選擇結束? 'SELECT * FROM b WHERE 3-8-2016 <= startDate AND endDate <='3-15-2016'OR'3-8-2016'> = startDate AND endDate> ='3-15-2016'OR'3 -816'> = startDate AND endDate <='3-15-2016'AND(startDate> = NOW()AND endDate> = NOW())OR'3-8-2016'<= startDate AND endDate> = '3-15-2016'和((''3-8-2016'在startDate和endDate之間)或('3-15-2016'在startDate和endDate之間))' – rel1x

+0

編號重新閱讀我的評論。 – Strawberry

回答

1

這是查詢的工作原理,並已經過任何其他職位空缺組合的測試。任何事情之後空缺。在現有開始之前,之後開始日期。結束日期在現有結束日期之前,之後。完全跨在另一個預訂之外。完全在另一個預訂。

select 
     r.RoomID 
    from 
     Room r 
     LEFT JOIN 
     (select 
       b.RoomID 
       from 
       booking b, 
       (select @parmStartDate := '2016-01-21', 
          @parmEndDate := '2016-01-23' ) sqlvars 
       where 
        b.EndDate >= @parmStartDate 
       AND b.StartDate <= @parmEndDate 
       AND ( timestampdiff(day, b.StartDate, @parmEndDate) 
        * timestampdiff(day, @parmStartDate, b.EndDate )) > 0) Occupied 
     ON r.RoomID = Occupied.RoomID 
    where 
     Occupied.RoomID IS NULL; 

樣品預訂數據創建我包括

BookID RoomID StartDate EndDate 
1  1  2016-02-03 2016-02-04 
2  1  2016-02-04 2016-02-08 
3  1  2016-02-12 2016-02-16 
4  1  2016-02-20 2016-02-28 

然後我用下面的預訂日期檢測,符合下列有效空缺VS衝突,並已經被佔用了上來。此測試僅適用於單人房間,但顯然適用於酒店的任何房間。

Both dates before anything on file... Room available 
2016-01-10 - 2016-01-15 

Both dates after anything on file... Room available 
2016-03-10 - 2016-03-15 

Occupied ID 1 -- Same start date 
2016-02-03 - 2016-02-04 

Occupied ID 2 -- Same start date, but less than existing occupied end date 
2016-02-04 - 2016-02-05 

Occupied ID 2 -- Same start, Exceeds end occupancy date 
2016-02-04 - 2016-02-09 

Occupied ID 3 -- Start before, but end date WITHIN existing booking 
2016-02-09 - 2016-02-13 

Available. The END Date is the START Date of the existing booking 
(Between 2 & 3 booking) 
2016-02-09 - 2016-02-12 

Occupied ID 3 -- Started within date, but end outside existing booking 
2016-02-15 - 2016-02-17 

Available. End of existing booking and nothing booked on 2/17 
2016-02-16 - 2016-02-17 

Occupied ID 3 -- Completely encompasses booking entry 
2016-02-11 - 2016-02-17 

Occupied ID 4 -- totally WITHIN another entry 
2016-02-21 - 2016-02-23 

現在來解釋發生了什麼。我使用了LEFT-JOIN並尋找NULL(即:與另一個預訂不衝突),這與您的NOT IN子查詢非常相似。所以我會跳過那部分。

首先是FROM子句。所以我不必像存儲過程那樣「聲明」變量,我正在通過@parmStartDate,@parmEndDate進行IN-LINE操作,併爲了聲明目的分配別名sqlvars。由於這返回一行,所以將笛卡爾應用於預訂表是沒有問題的。

from 
    booking b, 
    (select @parmStartDate := '2016-01-21', 
      @parmEndDate := '2016-01-23' ) sqlvars 

現在,WHERE子句。如果你的表有多年價值的預約時間後,客房的100的,這有可能會相當大快,所以我想只有那些日期在那裏現有的訂單會來到位這預啓動是

where 
     b.EndDate >= @parmStartDate 
    AND b.StartDate <= @parmEndDate 

至少,我只關心那些現有結帳日期至少是您嘗試查找空房時間的預訂。例如:您正在尋找7月4日的入住日期。爲什麼你會關心是否有人在2月,3月,4月等等退房?所以現在,你走多遠......你也只關心那些下一個現有預訂有開始日期最多的記錄您將要退房的那一天。所以,如果7月6日檢查,你不關心7月7日或之後的任何預訂。到現在爲止還挺好。

現在,我來了解如何知道房間是否被佔用。我在比較現有的開始日期和查找日期方面遇到了困難,並且得到了錯誤的答案,所以我不得不求助於日期數學,並將開始時間和結束時間以及結束時間進行比較以開始,如果乘數結果爲正數,則會出現衝突。

AND ( timestampdiff(day, b.StartDate, @parmEndDate) 
    * timestampdiff(day, @parmStartDate, b.EndDate )) > 0) 

既然我們已經知道,我們有可能的日期範圍內的記錄,這是在做任一方向衝突檢查全外面,裏面,衝突向左或向右衝突。它只是工作。

你將不得不看到它更好地理解它,這是我跑的查詢,所以你可以看看你自己的結果。只需插入您正在查找的相應開始/結束日期即可。

select 
     b.BookID, 
     b.RoomID, 
     b.StartDate, 
     b.EndDate, 
     @parmStartDate as pStart, 
     @parmEndDate as pEnd, 
     ( timestampdiff(day, b.StartDate, @parmEndDate) 
     * timestampdiff(day, @parmStartDate, b.EndDate )) <= 0 as Available, 
     ( timestampdiff(day, b.StartDate, @parmEndDate) 
     * timestampdiff(day, @parmStartDate, b.EndDate )) > 0 as Occupied 
    from 
     booking b, 
     (select @parmStartDate := '2016-01-21', 
       @parmEndDate := '2016-01-23' ) sqlvars 

好運...

+0

@rel1x,在SQL中修改了查詢並測試了所有具有簡化參數設置的組合,以防止複雜嵌入。 – DRapp

+0

我仍然不明白爲什麼你有更多的查詢比'where b.EndDate> = @parmStartDate AND b.StartDate <= @ parmEndDate'。這很好,並使用索引 - 哪些timestampdiff等不能 – Strawberry

+0

@Strawberry,而TIMESTAMPDIFF()不使用索引,WHERE WOULD的前一部分尋找最低日期來限定。在MySQL中,在工作臺中,我正在嘗試使用> =和<日期,但他們錯誤地回來了,例如計算數字,所以迫使日期函數起作用。從字面上看,例如:'2016-01-15'的開始被認爲大於'2016-02-04',所以我必須做一個解決方法。 – DRapp

3

查找範圍內免費客房的問題,一般的方法($ BOOKING_BEGIN < => $ BOOKING_END)會是這樣:

SELECT 
    rooms.room_id 
FROM 
    rooms 
LEFT JOIN 
    bookings 
    ON (
     bookings.room_id = rooms.room_id AND 
     NOT ( 
      (bookings.begin < $BOOKING_BEGIN and bookings.end < $BOOKING_BEGIN) 
      OR 
      (bookings.begin > $BOOKING_END and bookings.end > $BOOKING_END) 
      ) 
     ) 
WHERE 
    bookings.room_id IS NULL; 

它只是手段'把酒店的所有房間都拿走,然後加入已經預訂好的房間。如果爲空,則表示房間在給定範圍內免費(Join沒有找到現有預訂)。