酒店预订系统SQL查询:确定一个特定房间何时可用

时间:2011-04-09 18:03:21

标签: mysql sql join

我有一个酒店预订系统的查询,我需要找到特定房间可用的日期。 (这是一家精品酒店,人们预订特定的房间,所以他们在得到这个代码之前就知道了他们想要的确切房间。我所追求的结果是一个房间的细节,我在查询中指定的房间 - - 我不是在寻找多个房间的信息。

'availability'表架构很简单,每一行都是:

room_id
date_occupied - a single day that is occupied (like '2011-01-01')

因此,例如,如果一个房间从1月1日到1月5日被占用,则可用性表中会添加五行,每天占用一个房间。

这就是我想要解决的问题:

  • 查询在开始日期和结束日期之间查找特定房间的时间,类似于:
SELECT rooms.* FROM rooms, availability
 WHERE rooms.id = 123
   AND availability.room_id = rooms.id
   AND nothing between start_date and end_date is in availability.date_occupied
  • 我也在寻找一个类似的查询,我只想查看特定房间是否可用于开始日期和接下来的两天,例如:
SELECT rooms.* FROM rooms, availability
 WHERE rooms.id = 123
   AND availability.room_id = rooms.id
   AND start_date, start_date+1day and start_date+2days is not in availability.date_occupied

我有点卡住试图找出确切的联接。有什么建议?请注意,如果它有帮助,我完全可以使用可用性表的不同模式。无论是什么使查询最有效。

7 个答案:

答案 0 :(得分:1)

我认为最好的方法是简单地运行查询以查看房间是否被占用,并在没有返回任何行的情况下指示可用性。

因此,首先查询:

SELECT COUNT(id) AS occupied_days FROM availability
WHERE room_id = 123
      AND date_occupied BETWEEN @start_date AND @end_date;

如果occupied_days == 0,则房间可用。

对于第二个查询,只需将@end_date替换为DATE_ADD(@start_date, @num_days)

即可

对涉及联接的答案的一些评论:由于您将搜索限制为特定的room_id,因此将availability表加入room表是没有意义的,因为它没有提供必要的信息。所有这些LEFT JOIN只会使查询复杂化,可能会影响性能并且不会提供任何用途。

此外,虽然您可能会厌倦搜索占用率然后推断可用性的方法,但我猜这个查询是查询引擎优化的最快和最简单的,因为它基于单个列,如果编入索引,会使事情变得更快。

答案 1 :(得分:1)

我已经做了类似的事情,但是对于所有房间在给定范围内可用...我已经修改了你的概念,因为你的是一个特定的房间,但是这是原来的链接 Link to other registration

前提是拥有动态创建的“日期范围列表”,而无需实际创建表并显式插入行。这可以通过使用MySQL查询变量来完成。我从一个(任何)表中查询一个限制命令,我需要多少条目...你的预订表应该没问题...然后,我通过创建的特定日期查询,找不到房间。

select  JustDates.OpenDate
    from 
        ( SELECT 
                 @r:= date_add(@r, interval 1 day ) OpenDate
            FROM 
                 (select @r := current_date()) vars,
                availability limit 30 ) JustDates
    where
        JustDates.OpenDate not in
           ( select date_occupied 
                 from availability
                 where availability.room_id = OneYouWant
                   and availability.date_occupied = JustDates.OpenDate )
    order by 
       JustDates.OpenDate

如果你想要房间信息,你可以得到笛卡尔式的加入,因为你已经知道了房间,无论如何都会一样......

select Rooms.*
    from Rooms,
         ( AboveQuery ) OpenDates
    where Rooms.Room_ID = OneYouWant

您可以查看其他解决方案的链接,以获得有关如何从“current_date()”开始初始化@r的更多说明,并且LIMIT 30允许它因笛卡尔到达而导致30天后退出可用性表。 LIMITed 30记录中的每条新记录将继续将@r更新为1天。所以内部预先在这个例子中预测接下来的30天,那么WHERE NOT IN进入特定房间的可用性表并动态构建合格日期...这只会返回尚未占用的日期。

希望这能为您澄清这项技术。

答案 2 :(得分:0)

(这是我根据OP对标准的澄清更新的答案)

Eric,看看以下内容:

我的测试架构

mysql> desc rooms;
+-------+---------+------+-----+---------+----------------+
| Field | Type    | Null | Key | Default | Extra          |
+-------+---------+------+-----+---------+----------------+
| id    | int(11) | NO   | PRI | NULL    | auto_increment |
+-------+---------+------+-----+---------+----------------+
1 row in set (0.01 sec)

mysql> desc availability;
+---------------+---------+------+-----+---------+-------+
| Field         | Type    | Null | Key | Default | Extra |
+---------------+---------+------+-----+---------+-------+
| rooms_id      | int(11) | NO   | MUL | NULL    |       |
| occupied_date | date    | NO   |     | NULL    |       |
+---------------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)

我的测试数据

mysql> select * from rooms;
+-----+
| id  |
+-----+
| 123 |
+-----+
1 row in set (0.00 sec)

mysql> select * from availability;
+----------+---------------+
| rooms_id | occupied_date |
+----------+---------------+
|      123 | 2011-05-04    |
+----------+---------------+
1 row in set (0.00 sec)

我的测试查询

mysql> select rooms.* from rooms left outer join availability on rooms.id = availability.rooms_id where rooms.id=123 and availability.occupied_date not between '2011-05-03' and '2011-05-06';
Empty set (0.00 sec)

mysql> select rooms.* from rooms left outer join availability on rooms.id = availability.rooms_id where rooms.id=123 and availability.occupied_date not between '2011-05-05' and '2011-05-06';
+-----+
| id  |
+-----+
| 123 |
+-----+
1 row in set (0.00 sec)

第一个测试查询显示用户在房间被占用时在5/3和5/6之间搜索房间123。第二个查询显示用户在房间可用时在5/5和5/6之间搜索房间123。

这里缺少的成分是LEFT OUTER加入。无论是否存在“表B”(可用性)中的相应记录,这都从“表A”(房间)返回值。您只想要在可用性中不存在的情况,因此为了确保这一点,您应该在结果记录中包含可用性列,并指定要求availability.xxx为NULL的WHERE子句。我的测试/示例查询中缺少最后一点,所以请试一试,如果您仍需要帮助,请告诉我。

答案 3 :(得分:0)

听起来很容易在房间和可用性上进行内部连接。 请忘记'where'类型隐式连接并使用显式连接。 这样,查询更容易阅读,因为您将连接条件和选择条件分开。

首先确保您在字段availability.room_id上有索引 并确保rooms.id是房间的主要钥匙。

select * from rooms where id not in
  (select rooms.id from rooms
   inner join availability on (rooms.id = availability.room_id)
   where availability.date_occupied between StartDate and EndDate)
and rooms.id = 123

如果您想进一步测试,可以添加额外的条款。

...Previous query +
  and rooms.smoking = 0
  and rooms.number_of_beds = 2

答案 4 :(得分:0)

SQL 2008解决方案

create table availability
(
room_id int,
date_occupied datetime
)
go

insert into availability values(212,convert(date,DATEADD(d,2,GETDATE())));
insert into availability values(202,convert(date,DATEADD(d,5,GETDATE())));
insert into availability values(209,convert(date,DATEADD(d,7,GETDATE())));
insert into availability values(212,convert(date,DATEADD(d,9,GETDATE())));
insert into availability values(202,convert(date,DATEADD(d,10,GETDATE())));
insert into availability values(202,convert(date,DATEADD(d,20,GETDATE())));
go

CREATE FUNCTION GetMonthTable
(
    @d datetime
) RETURNS @days TABLE
(
    [Date] datetime,
    [Day] varchar(20)
)
BEGIN
    DECLARE @d1 datetime, @d2 datetime, @d3 datetime
    SELECT
    @d1 = DATEADD(mm, DATEDIFF(mm, 0, @d), 0),
    @d2 = DATEADD(dd, -1, DATEADD(mm, DATEDIFF(mm, 0, @d) + 1, 0))

    WHILE @d1 <= @d2
    BEGIN
    INSERT INTO @days SELECT @d1, DATENAME(DW, @d1)
    SELECT @d1 = DATEADD(dd, 1, @d1)
    END
    RETURN
END
go

select A.Date,
       A.Day, 
       convert(bit,isnull((B.date_occupied-B.date_occupied),1))Avalilability 
from        GetMonthTable(getdate()) A
left join   (Select * from availability where room_id=212) B
on A.Date=b.date_occupied

抱歉格式化。我是这个网站的新手

答案 5 :(得分:0)

查询查找具体的时间    房间开始和之间    结束日期:

SELECT r.*
FROM rooms r
  LEFT JOIN availability av
    ON av.room_id = r.id
WHERE r.id = 123
  AND av.date_occupied BETWEEN start_date AND end_date
GROUP BY r.id
HAVING count(av.room_id) = 0 

另一种方式:

SELECT *
FROM rooms
WHERE id = 123
  AND NOT EXISTS
    ( SELECT 1
      FROM availability
      WHERE date_occupied BETWEEN start_date AND end_date
        AND room_id = 123
    ) 

关于第二个问题,只需将end_date更改为

即可
DATE_ADD( start_date, INTERVAL 2 DAY)

(start_date + INTERVAL 2 DAY)

答案 6 :(得分:0)

如果在数据库中保留日历表,则查询很简单。请查看this answer,了解如何创建日期表的最小示例。这个答案的其余部分假定该特定表的存在。

用于查找特定房间在开始日期和结束日期之间何时可用的查询:

select datum as available_date
  from calendar c
 where c.datum between date '2011-01-01' and date '2011-01-10'
   and c.datum not in(
        select occupied_date 
          from availability
         where room_id = 1);

该查询将返回指定范围内未占用room_id=1的所有日期。

您可以扩展上述查询以查找:
如果特定房间可用于开始日期和接下来的两天

select case when count(*) = 3 then 'Yes' else 'Nope' end as available
  from calendar c
 where c.datum between date '2011-01-06' and date '2011-01-06' + interval '2' day
   and c.datum not in(
        select occupied_date 
          from availability
         where room_id = 1);

当指定数据范围内的任何天数都没有被占用时,该查询将返回“是”。数字3对应于日期范围内的天数(开始日期+2天)。

最终注释/警告:如果您提供不存在的room_id,则上述查询将不起作用。如果您的应用程序构建SQL,这不是问题。