如何根据MySQL中的日期范围查询可用的物品租赁?

How to query available item leases based on a date range in MySQL?

我们的业务是在旅行时向客户出租国际 phone 号码。当客户下订单时,我们希望根据客户的 start_date 和 end_date 以及尚未占用的号码,向客户显示其预订日期可用的 phone 号码。[=12] =]

由于这些 phone 号码已租出,我需要 select 从 table 仅那些尚未租出的号码会干扰当前客户日期。

我也不想在结束日期后 7 天之前出租任何 phone 号码。意思是,如果客户预订了 1-1-2020 到 1-20-2020 的 phone 号码,我不希望这个 phone 号码在 1-27-2020 之前被其他客户预订.我希望 phone 号码有 7 天 window 是清晰的。

我有一个带有 phone 号码的 table 和一个带有与 phone 号码 table 相关的订单的 table 通过 phone_number_id。订单 table 有当前客户 start_date 和 end_date 旅行,但尚未保存 phone 号码 ID。订单 table 还具有所有其他客户旅行日期的 start_date 和 end_date,以及 phone_number_id 已达到其旅行日期的 assigned/booked。

当尝试 select 当前客户日期可用的 phone 号码时,MySQL 查询会是什么样子?

我现在构建以下查询

SELECT x.id
     , x.area_code
     , x.phone_number
     , y.start_date
     , y.end_date 
  FROM vir_num_table x
  LEFT 
  JOIN orderitemsdetail_table y
    ON y.vn_id = x.id 
 WHERE y.start_date BETWEEN '2020-01-11' AND '2020-01-18' 
    OR y.start_date IS NULL

我已经构建了这个查询,但卡在这里如何添加 end_date 逻辑。

如有任何帮助,我们将不胜感激!提前致谢。

我处理这个问题的方法是从概念上看,是所有 phone 数字集的叉积,以及预订时间范围,然后排除那些有冲突保留。

冲突将是一个重叠,现有保留有一个 start_date beforeend 并且end_date 开始 之后

我会做一个反连接模式,像这样:

 SELECT pn.phone_number
   FROM phone_number pn

   LEFT
   JOIN reservation rs
     ON rs.phone_number  = pn.phone_number
    AND rs.start_dt     <= '2019-12-27' + INTERVAL +7 DAY
    AND rs.end_dt        > '2019-12-20' + INTERVAL -7 DAY
  WHERE rs.phone_number IS NULL 

这实际上是说从 phone 号中获取所有行,以及从保留中匹配的行(重叠的行),然后排除所有匹配的行,只留下 phone_number 行没有匹配项。

我们可以将 < 测试设为 <= 或减去 8 天,以调整之前的“7 天” window;我们可以在 运行 通过测试用例查询时进行调整,

我们可以使用 NOT EXISTS 和相关子查询来获得等效的结果。有些人发现这比 ant-join 更容易理解,但它本质上是相同的查询,做同样的事情,从 phone_number 获取所有行,但排除保留中有匹配(重叠)行的行

 SELECT pn.phone_number
   FROM phone_number pn
  WHERE NOT EXISTS 
        ( SELECT 1 
            FROM reservation rs
           WHERE rs.phone_number  = pn.phone_number 
             AND rs.start_dt     <= '2019-12-27' + INTERVAL +7 DAY
             AND rs.end_dt        > '2019-12-20' + INTERVAL -7 DAY
        )

Whosebug 上有几个关于检查日期范围是否重叠的问题。

参见例如


编辑

基于作为对问题的编辑添加的 SQL,我会这样查询:

SELECT pn.`id`
     , pn.`area_code`
     , pn.`phone_number`
  FROM `vir_num_table` pn
  LEFT
  JOIN `orderitemsdetail_table` rs
    ON rs.vn_id        = pn.id
   AND rs.start_date  <= '2020-01-18' + INTERVAL +7 DAY
   AND rs.end_date     > '2020-01-11' + INTERVAL -7 DAY
 WHERE rs.vn_id IS NULL

两个"tricky"部分。首先是反加入,了解它是如何工作的。 (外部连接,return 来自 vir_num_table 的所有行,但排除在保留中具有匹配行的任何行。第二个棘手的部分是检查重叠,提出条件:r.start <= p.end AND r.end >= p.start,然后调整我们是否要将相等作为重叠包括在内,并调整额外的 7 天(对我来说最简单的是减去从提议的预订开始后 7 天)

...现在我想到我们需要在预订期结束时增加 7 天的保护期,哦!

根据您添加的信息,您应该不需要检查 phone 个已被预订的号码的开始日期。

  • 您的客户为您提供了开始日期和结束日期。
  • 您只能在上次租约结束后 7 天出租 phone 个号码

您需要做的就是取回 phone 个号码,该号码可以: - 未出租,因此不在订单中 table - 或者 end_date 是新客户开始日期前 7 天。

给你:

    SELECT 
    `main_table`.`id`,
    `main_table`.`area_code`,
    `main_table`.`phone_number`,
    `orderitemsdetail_table`.`start_date`,
    `orderitemsdetail_table`.`end_date`
FROM
    `vir_num_table` AS `main_table`
        LEFT JOIN
    `orderitemsdetail_table` AS `orderitemsdetail_table` ON main_table.id = orderitemsdetail_table.vn_id
WHERE
    (DATE_ADD(orderitemsdetail_table.end_date, INTERVAL 7 DAY) < '<CUSTOMER START DATE>'
    AND orderitemsdetail_table.start_date > '<CUSTOMER END DATE>')

    OR orderitemsdetail_table.id IS NULL

这是一个查询和排序算法,用于选择最佳 phone 数量 selection 以获得最大的利用效率(即尽可能接近每次使用前后的 7 天)。

我将其设置为开放式权重为 9,因此 "near perfect" 适合(前后 7-8 天)将在开放式数字之前 selected。这将略微提高效率,因为开放号码可以容纳任何预订。您可以根据需要进行调整。例如,如果将其设置为 0,它总是 select 首先打开数字。

SELECT ph.phone_number,
    COALESCE(
        MIN(
            IF(res.end_date > res.start_date > '2020-01-18',
            NULL, -- ignore before-comparison for reservations starting and ending after date range
            DATEDIFF('2020-01-11', res.end_date)
        ), 9) AS open_days_before,
    COALESCE(
        MIN(
            IF(res.start_date < res.end_date < '2020-01-11',
            NULL, -- ignore after-comparison for reservations starting and ending before date range
            DATEDIFF(res.start_date, '2020-01-18')
        ), 9) AS open_days_after
FROM phone_number ph
LEFT JOIN reservation res
    ON res.phone_number = ph.phone_number
    AND res.end_date >= CURRENT_DATE() - INTERVAL 6 DAY
GROUP BY ph.phone_number
HAVING open_days_before >= 7
    AND open_days_after >= 7
ORDER BY open_days_before + open_days_after
LIMIT 1

编辑:更新以添加分组,因为我意识到这是一个聚合问题。

编辑 2:错误修复,将 MAX 更改为 MIN

编辑 3:添加 res.end_date >= CURRENT_DATE - INTERVAL 6 DAY 以忽略过去的预订,限制汇总数据并将 6 天前和新订单开始之间没有预订的 phone 号码视为 "open on the front-end"

编辑 4:添加 IF 条件以消除给定前后比较范围之外的预留(例如,比较 selected 范围后的预留影响 "open days before" 数量),以防止负数,除非与 selected 范围重叠。