【问题标题】:hotel reservation system SQL query: identify when one specific room is available酒店预订系统 SQL 查询:确定一个特定房间何时可用
【发布时间】:2011-04-09 18:03:20
【问题描述】:

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

“可用性”表架构很简单,每一行是:

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

我在试图找出确切的连接时有点卡住了。有什么建议么?请注意,如果它有帮助,我对可用性表的不同模式完全开放。使查询工作最有效的因素。

【问题讨论】:

    标签: mysql sql join


    【解决方案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 JOINs 只会使查询复杂化,可能会降低性能并且没有任何用处。

    另外,虽然您可能对搜索入住率然后推断可用性的方法感到犹豫,但我猜想这个查询是迄今为止查询引擎优化最快和最容易的,因为它基于单个列,如果被索引,将会使事情变得更快。

    【讨论】:

    • 谢谢你——最终,这个答案的优雅使它成为适合我的答案。
    【解决方案2】:

    我做了类似的事情,但是对于给定范围内的所有可用房间...我已经为您修改了概念,因为您的房间是一个特定的房间,但这是原始链接 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 由于笛卡尔到可用性表。 LIMITed 30 条记录中的每条新记录都会将@r 更新为多 1 天。因此,在此示例中,内部预先获取接下来的 30 天,然后 WHERE NOT IN 转到特定房间的可用性表并动态构建合格日期...这只会返回尚未占用的日期。

    希望这可以为您阐明这项技术。

    【讨论】:

      【解决方案3】:

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

      埃里克,看看下面的内容:

      我的测试架构

      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 联接。这将返回来自“表 A”(房间)的值,无论“表 B”(可用性)中是否存在相应的记录。您只想要它们不存在于可用性中的情况,因此为确保这一点,您应该在结果记录中包含可用性列,并指定一个要求可用性的 WHERE 子句。xxx 为 NULL。我的测试/示例查询中缺少最后一点,因此请尝试一下,如果您仍然需要帮助,请告诉我。

      【讨论】:

      • 您好 AJ,感谢您的回复,但我正在寻找一家精品酒店,人们可以在其中预订特定房间(即,他们在获得此代码之前就知道他们想要的确切房间)。要回答您的问题,哪个房间可用是很重要的。我的问题是根据我提出的问题找出房间被占用的日期。谢谢。
      • @Eric - 不用担心,感谢您的澄清。可能想在你原来的问题中发布这个。给我几分钟,我会处理一下。
      【解决方案4】:

      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】:

      对房间和可用性进行内部连接听起来很容易。 请忘记“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
      

      等等

      【讨论】:

      • 嗨,Johan,感谢您的回复,但我不确定它是否完全符合目标。看起来您并没有在查询中同时考虑开始日期和结束日期,而且看起来您还返回了任意数量的房间。需要明确的是,在执行任何此代码之前,我已经知道房间号,因此应该只使用“房间”表中的一行。这就是为什么我的伪代码示例有“rooms.id = 123”——在这种情况下,客户已经对 123 号房间感兴趣,我只需要确定它是否在他们的日期开放。
      • 好的,编辑答案以反映您的 cmets
      【解决方案6】:

      查询以查找特定的 房间在开始和 结束日期:

      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)
      

      【讨论】:

        【解决方案7】:

        如果您在数据库中保留一个日历表,您的查询就很简单。查看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,这不是问题。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-03-27
          • 2011-08-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多