简体   繁体   English

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

[英]hotel reservation system SQL query: identify when one specific room is available

I have a query for a hotel reservation system and I need to find dates when a specific room is available. 我有一个酒店预订系统的查询,我需要找到特定房间可用的日期。 (It's a boutique hotel where people reserve specific rooms, so they know the exact room they want before they get to this code. The result I'm after is the detail for ONE room, the one I specify in the query-- I am not looking for info on multiple rooms. ) (这是一家精品酒店,人们预订特定的房间,所以他们在得到这个代码之前就知道他们想要的确切房间。 我想要的结果是一个房间的细节,我在查询中指定的那个 - 我是不寻找多个房间的信息。

The 'availability' table schema is simple, each row is: 'availability'表模式很简单,每一行都是:

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

So, if, for example, a room is occupied from January 1 to January 5, five rows are added to the availability table, one for each day the room is occupied. 因此,例如,如果从1月1日到1月5日占用一个房间,则在可用性表中添加五行,每天占用一个房间。

Here's what I'm trying to work out: 这就是我想要解决的问题:

  • the query to find when a specific room is available between a start and end date, sort of like: 用于查找特定房间何时在开始日期和结束日期之间可用的查询,类似于:
 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 
  • I'm also seeking a similar query where I just want to see if a specific room is available for the start date and the following two days, something like: 我也在寻找一个类似的查询,我只想查看特定房间是否可用于开始日期和接下来的两天,例如:
 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 

I'm a bit stuck trying to figure out the exact joins. 我有点卡住试图找出确切的连接。 Any suggestions? 有什么建议么? Note that if it helps, I'm totally open to a different schema for the availability table. 请注意,如果它有帮助,我完全可以使用可用性表的不同模式。 Whatever makes the queries work most efficiently. 无论是什么使查询最有效。

I think the best way to do this is to simply run a query to see if a room is occupied and indicate availability in the event that no rows are returned. 我认为最好的方法是简单地运行查询以查看房间是否被占用,并在没有返回任何行的情况下指示可用性。

As such, for you first query: 因此,为您首先查询:

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

If occupied_days == 0 then the room is available. 如果occupied_days == 0则房间可用。

For your second query, just replace @end_date with DATE_ADD(@start_date, @num_days) 对于第二个查询,只需将@end_date替换为DATE_ADD(@start_date, @num_days)

Just some comments on answers involving a join: since you're limiting your search to a specific room_id , it makes no sense to join the availability table to the room table, since it's providing no necessary information. 只是对涉及room_id答案的一些评论:由于您将搜索限制在特定的room_id ,将availability表加入room表是没有意义的,因为它没有提供必要的信息。 All these LEFT JOIN s are just complicating the query, potentially impairing performance and providing nothing of any use. 所有这些LEFT JOIN只会使查询复杂化, 可能会影响性能并且不提供任何用途。

Also, while you may baulk at the approach of searching for occupancy and then inferring availability, I would guess that this query is by far the fastest and easiest for the query engine to optimize, since it's based on a single column which, if indexed, will make things even faster. 此外,虽然您可能会考虑搜索占用率然后推断可用性,但我猜这个查询是查询引擎优化的最快和最简单的,因为它基于单个列,如果已编入索引,会让事情变得更快。

I've done something similar, but for All rooms available within a given range... I've modified the concept for yours since yours is a specific room, but heres the original link Link to other registration 我已经做了类似的事情,但是对于所有房间在给定范围内可用...我已经修改了你的概念,因为你的是一个特定的房间,但继承人的原始链接链接到其他注册

The premise is to have a dynamically created "date range list" without having to actually create a table and explicitly insert the rows. 前提是有一个动态创建的“日期范围列表”,而无需实际创建表并显式插入行。 This can be done by using MySQL query variables. 这可以通过使用MySQL查询变量来完成。 I query from a(any) table with a limit command to how many entries I need.. your reservation table should be fine... Then, I query from that via the specific dates that are created where the room is NOT found... 我从一个(任何)表中查询一个限制命令,我需要多少条目...你的预订表应该没问题...然后,我通过创建的特定日期查询,找不到房间。 。

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

If you want the room information, you can just get a Cartesian join against it since you already know the room, and all would be the same anyhow... 如果你想要房间信息,你可以得到一个笛卡尔式的加入,因为你已经知道了房间,所有的都是一样的...

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

You can check the link to the other solution for more clarification on how the @r starting from the "current_date()" gets initialized, and the LIMIT 30 lets it go out for 30 days as a result of the Cartesian to the availability table. 您可以检查指向其他解决方案的链接,以便更详细地了解从“current_date()”开始的@r如何初始化,并且LIMIT 30允许它因笛卡尔到可用性表而退出30天。 Each new record in the LIMITed 30 records will keep updating the @r to 1 more day. LIMITed 30记录中的每条新记录将继续将@r更新为1天。 So the inner pre-gets in this example the next 30 days, THEN the WHERE NOT IN goes to the availability table for the specific room and qualified dates dynamically built... This would only return the dates NOT already occupied. 所以内部预先在这个例子中接下来的30天,然后WHERE NOT IN进入特定房间的可用性表和动态构建的合格日期......这只会返回尚未占用的日期。

Hopefully this clarifies the technique for you. 希望这能为您澄清这项技术。

(Here's my updated answer based on the OP's clarification of the criteria) (这是我根据OP对标准的澄清更新的答案)

Eric, take a look at the following: 埃里克,看看以下内容:

My Test Schema 我的测试架构

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)

My Test Data 我的测试数据

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)

My Test Queries 我的测试查询

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)

The first test query shows a user searching for room 123 between the dates of 5/3 and 5/6, when the room is occupied. 第一个测试查询显示用户在房间被占用时在5/3和5/6的日期之间搜索房间123。 The second query shows a user searching for room 123 between the dates of 5/5 and 5/6, when the room is available. 第二个查询显示用户在房间可用时在5/5和5/6的日期之间搜索房间123。

The missing ingredient here was a LEFT OUTER join. 这里缺少的成分是LEFT OUTER加入。 This returns values from "table A" (rooms) whether or not corresponding records in "table B" (availability) exist. 无论是否存在“表B”(可用性)中的相应记录,这都从“表A”(房间)返回值。 You're only wanting cases where they don't exist in availability, so to ensure this, you should include a column in availability in your result records, and specify a WHERE clause that requires that availability.xxx IS NULL. 您只想要在可用性中不存在的情况,因此为了确保这一点,您应该在结果记录中包含可用性列,并指定要求availability.xxx为NULL的WHERE子句。 This last bit is missing from my test/example queries, so give it a try on your end and let me know if you still need help. 我的测试/示例查询中缺少最后一点,因此请尝试一下,如果您仍需要帮助,请告诉我。

Sounds easy enough do an inner join on rooms and availability. 听起来很容易做到内部连接房间和可用性。 Please forget about the 'where' type implicit join and use an explicit join. 请忘记'where'类型隐式连接并使用显式连接。 That way the query is easier to read because you separate the join condition and the selection criteria. 这样,查询更容易阅读,因为您将连接条件和选择条件分开。

First make sure you have an index on the field availability.room_id And make sure the rooms.id is the primary key on rooms. 首先确保您在字段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

If you want to do further test you can add an extra clauses. 如果您想进一步测试,可以添加一个额外的条款。

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

etc. 等等

SQL 2008 Solution 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

Sorry for the formating. 对不起,格式化。 I am new to this site 我是这个网站的新手

Query to find when a specific room is available between a start and end date: 查询在开始日期和结束日期之间查找特定房间的时间:

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 

Another way: 其他方式:

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
    ) 

For your second question, just change the end_date to 对于第二个问题,只需将end_date更改为

DATE_ADD( start_date, INTERVAL 2 DAY)

or to 或者

(start_date + INTERVAL 2 DAY)

Your queries are simple if you keep a calendar table in your database. 如果在数据库中保留日历表,则查询很简单。 Have a look in this answer for a minimal example of how to create a table of dates. 请查看此答案 ,了解如何创建日期表的最小示例。 The rest of this answer assumes the existance of that particular table. 本答案的其余部分假定该特定表的存在。

the query to find when a specific room is available between a start and end date: 查询在开始日期和结束日期之间查找特定房间的时间:

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

That query will return all the dates within your specified range where room_id=1 isn't occupied. 该查询将返回指定范围内未占用room_id=1所有日期。

You can extend the above query to find: 您可以扩展上面的查询来查找:
if a specific room is available for the start date and the following two days 如果特定房间可用于开始日期和随后两天

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

That query will return 'Yes' when none of the days within the specified data range are occupied. 当指定数据范围内的任何天数都未被占用时,该查询将返回“是”。 The number 3 corresponds to the number of days within the date range (start day + 2 days). 数字3对应于日期范围内的天数(开始日+ 2天)。

A final note/warning: The above queries will not work if you supply a non-existing room_id. 最后的注释/警告:如果您提供不存在的room_id,则上述查询将不起作用。 This isn't a problem if your application builds the SQL. 如果您的应用程序构建SQL,这不是问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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