酒店预订系统,SQL查询以查找免费房间的问题

时间:2019-05-12 09:38:13

标签: mysql sql

这是我的数据库架构

SELECT r.id FROM room r LEFT JOIN reservation rs ON rs.room_id = r.id
WHERE (rs.date_in < :dateIn AND rs.date_out <= :dateOut)
   OR (rs.date_in >= :dateIn AND rs.date_out > :dateOut)
   OR rs.id IS NULL;

查询以显示没有预订的房间

例如在1号房间中进行从2019-07-12到2019-07-14的预订后,然后使用dateIn 2019-07-12和dateOut 2019-07-14进行sql查询,作为响应我得到了房间否1,这是不正确的,因为已在此扩展时间中进行了保留。

4 个答案:

答案 0 :(得分:1)

您要排除有冲突的预订。

让我们首先考虑请求的期限(A ... B)和假设的保留(C ... D),我们可以绘制所有可能性的图,并发现存在几种冲突可能性:

A.....B  C.....D       OKAY: note B < C never happens otherwise
A........C..B..D       NOT OKAY    (B inside C..D)
A........C.....D..B    NOT OKAY    (C inside A..B, D inside A..B)
         C..A..D..B    NOT OKAY    (A inside C..D, D inside A..B)
         C.A.B.D       NOT OKAY    (A inside C..D, B inside C..D)
         C.....D A..B  OKAY: note A > D never happens otherwise
A...B                  OKAY        (no C...D reservation at all)

因此,基本上任何一个区间的元素都不能在另一个区间之内:

NOT (
    (:dateIn BETWEEN rs.date_in AND rs.date_out)
    OR
    (:dateOut BETWEEN rs.date_in AND rs.date_out)
    OR
    (rs.date_in BETWEEN :dateIn AND :dateOut)
    OR
    (rs.date_out BETWEEN :dateIn AND :dateOut)
)

并且这必须包括查询失败(根本没有rs条目)。

看到同一张图,我们还看到我们可以只请求B

rs.date_in > :dateOut OR rs.date_out < :dateIn

并且由于您想要没有冲突的房间,因此您必须将上面的查询放在旨在查找冲突的JOIN子句中,并带有LEFT JOIN和“无冲突”请求(rs WHERE中的info为NULL),以捕获JOIN子句失败的地方:

SELECT rooms.* 
    FROM rooms 
    LEFT JOIN reservations ON
    (
        rooms.id = reservations.room_id
        AND (
            NOT (reservations.date_in > :dateOut OR reservations.date_out < :dateIn)
        )
    )
) WHERE rooms.id = :id AND reservations.room_id IS NULL

可以简化JOIN子句,因为NOT (A OR B)等于NOT A AND NOT B,而NOT ><=

    (
        rooms.id = reservations.room_id
        AND reservations.date_in  <= :dateOut 
        AND reservations.date_out >= :dateIn
    )

这立即告诉我们reservations的便捷索引:

    CREATE INDEX reservations_chk ON reservations(room_id, date_in, date_out, rs_id);

您要对此进行测试,并验证预订在一天之内结束而请求在同一天开始的情况下发生的情况(预订在请求结束时开始的那一天也是如此)。然后,根据酒店政策,您可能希望从一个或两个限制子句中删除相等性。这是因为数据库的日期在午夜“开始”和“结束”,但是现实中的预订可能始于例如11:00,结束于10:00,以便进行房间清理和准备。


由于这样的查询很可能包含关键字SELECT,JOIN,reservation,room,available等,因此您可以在Google上搜索并找到thisthis或{{3} },可能会引起关注(我只粗略地看过它们-)。

答案 1 :(得分:0)

您的WHERE子句不正确,请尝试:

WHERE rs.date_out <= :dateIn OR rs.date_in >= :dateOut OR rs.id IS NULL;

这意味着:当前访客将在此访客到达之前离开,或者下一访客将在此访客离开之后到达。

答案 2 :(得分:0)

您可以使用NOT EXISTS并应用条件:

SELECT r.id FROM room r 
WHERE NOT EXISTS (
  SELECT 1 FROM reservation rs
  WHERE rs.room_id = r.id AND (
    (rs.date_in BETWEEN :dateIn AND :dateOut) OR
    (rs.date_out BETWEEN :dateIn AND :dateOut) OR
    (rs.date_in < :dateIn AND rs.date_out > :dateOut)
  )
)

答案 3 :(得分:0)

我将使用NOT EXISTS和以下逻辑:

select r.id
from room r
where not exists (select 1
                  from reservation rs 
                  where rs.room_id = r.id and
                        rs.date_in < :dateOut and
                        rs.date_out >= :dateIn
                 );

重叠时间间隔的逻辑实际上很简单-但可能很棘手。当满足以下条件时,两个时间间隔重叠:

  • 第一个开始,第二个结束。
  • 第一个在第二个开始之后结束。

是否包括目标取决于您的特定业务规则。上述逻辑允许某人在与其他人结帐的同一日期签入。

您可以将其表达为left join。我发现NOT EXISTS可以更好地准确捕获您要查找的内容-在特定时期内没有预订的房间。