【问题标题】:Finding free slots in a booking system在预订系统中查找空闲时段
【发布时间】:2010-10-21 08:10:35
【问题描述】:

my question about searching for date ranges 中,我尝试简化问题,但无意中提出了一个不同且更简单的问题。

我不会通过编辑使这个问题复杂化,而是要问我真正想要的问题。

我有两张表 Property 和 Booking。预订具有属性的外键以及开始和结束日期。

用户正在搜索空闲时段并提供所需的持续时间(以天为单位)。他们还提供了一系列他们感兴趣的开始日期。因此,搜索将按照以下方式进行: “找到我想要从 5 月开始的 3 天时段的所有属性。”

现在我可以通过以下方式做到这一点: 1. 为每个可能的开始日运行 31 个查询 2. 查找 5 月的所有预订,将它们压缩成一个由 31 个布尔值组成的数组,代表天数,然后循环查找时段。

我假设 (2) 在大多数情况下更有效。有没有更好的算法?有没有纯SQL的解决方案。

我将使用 Django,而且我的数据集很小,所以我可能会接受一个“愚蠢”的方法,但我很想知道最好的算法是什么样的。

【问题讨论】:

    标签: sql mysql django date


    【解决方案1】:

    可能对您的应用程序来说太过分了 - 但是:

    以使“写入”过程更加复杂为代价来改进搜索的一种相对简单的方法是更改​​ Booking 表以使其成为“可用性”表。

    添加一个布尔列来指示空位是空闲还是预订(或者最好还是输入预订它的客户的 id,如果空位空闲则使用 0)。

    从 2009 年 1 月 1 日 -> 20 年 12 月 31 日开始一个免费时段??

    当您获得预订时,将空闲插槽分为 3 个(两个插入和一个更新)、已预订插槽和两个可用插槽。

    继续这样做,随着时间框架变得更加分散,预订过程将包括以下之一:

    • 将整个“可用槽”分配给某人(一次更新)
    • 将“可用插槽”拆分为两个(一个更新和一个插入)
    • 如果有人从可用位置预订了中间部分,则将一个位置分成 3 个(如上所述)。

    管理起来并不复杂,搜索过程变成了一个简单的查询:查找所需时间范围内可用的任何空档(booked=false 或 customerid=0,无论您采用哪种方式) where enddate - startdate > = 你想要的天数。

    它使预订/可用性表的大小增加了一倍,并使预订变得不那么简单,但代价是搜索过程尽可能简单。

    【讨论】:

    • 非常聪明的方法,但我也需要预订数据用于许多其他目的。除了我当前的预订模式之外,我还可以为每个属性创建一个可用性表。从某种意义上说,这使我的数据非规范化以使一种类型的搜索更容易,但在这种情况下可能算作过早的优化。
    • 您的原始预订数据仍然存在 - 它只是有一个额外的列,其中包含“已预订”布尔值或客户 ID。如果您使用 wherebooked=true 或 customerid>0 搜索可用性表,您将拥有与原始表中相同的记录集。这就是为什么它将桌子的大小加倍,它包括预订和可用的位置。
    • +1 可能不是 OP 正在寻找的解决方案,但非常简洁。
    • 我对这个问题思考得越多,我就越意识到这比我能想到的所有其他解决方案都更干净、更简单!
    • 大多数操作系统内存管理器都是这样工作的,只存储可用内存块,并将最小的可能连续可用块分配给新的块请求(或预订,就此而言) ,让调用应用程序担心存储“预定”块并释放它们。
    【解决方案2】:

    表定义会有所帮助,但在这里。这应该适用于 MS SQL Server,但是一旦您了解其背后的想法,将其转换为 MySQL 应该是一项微不足道的任务。

    日历表只是一个标准实用程序表,其中包含对您的数据库有用的所有日期。如果您还没有,我建议您创建一个并填充它。

    CREATE TABLE Calendar
    (
         date        DATETIME     NOT NULL,
         is_holiday  BIT          NOT NULL,
         -- any other columns that might be relevant for your business
         CONSTRAINT PK_Calendar PRIMARY KEY CLUSTERED (date)
    )
    

    然后,您需要在表格中填充可能对您的业务有意义的任何日期。即使您回到 100 年并向前 100 年,表中的行数仍然少于 75K 行,并且它是按日期聚集的,因此它应该快速且易于使用。它使许多基于日期的查询变得更加简单。

    SELECT
         P.property_id,
         C.date
    FROM
         Calendar C
    JOIN Properties P ON 1=1
    WHERE
         C.date BETWEEN @search_start_date AND @search_end_date AND
         NOT EXISTS
         (
              SELECT
                   *
              FROM
                   Bookings B
              WHERE
                   B.property_id = P.property_id AND
                   B.start_date <= DATEADD(dy, @slot_length, C.date) AND  -- You would use MySQLs date function
                   B.end_date   >= C.date
         )
    

    或者:

    SELECT
         P.property_id,
         C.date
    FROM
         Calendar C
    JOIN Properties P ON 1=1
    LEFT OUTER JOIN Bookings B ON
                   B.property_id = P.property_id AND
                   B.start_date <= DATEADD(dy, @slot_length, C.date) AND  -- You would use MySQLs date function
                   B.end_date   >= C.date
    WHERE
         C.date BETWEEN @search_start_date AND @search_end_date AND
         B.booking_id IS NULL
    

    【讨论】:

    • 这只是一个日期表。它在那里,以便您可以以基于集合的方式处理日期。我会在答案中添加一些示例 DDL。
    • 我有一种可怕的感觉,我只是不够聪明,无法理解这个答案! 'JOIN Properties P ON 1=1' 行有什么作用?
    • 您将获得两张表的笛卡尔积。基本上,从每个可能日期的每种产品开始,因为如果预订中不存在一行,那么您想要包含它。一旦我们确定了所有可能的住宿日期,我们就会删除所有已预订的日期。
    猜你喜欢
    • 2017-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-21
    • 2020-10-17
    • 1970-01-01
    • 2011-03-06
    • 1970-01-01
    相关资源
    最近更新 更多