如何根据MySQL中的日期范围查询可用的项目租约?

问题描述 投票:0回答:3

我们有一家公司在旅行时向客户出租国际电话号码。当客户下订单时,我们希望根据其开始日期和结束日期以及尚未占用的号码向客户显示其预订日期的可用电话号码。

由于这些电话号码已租出,所以我只需要从表中选择那些尚未租出的号码,以免影响当前客户的日期。

我也不想在结束日期后的7天之前出租任何电话号码。意思是,如果某个客户预订了1-1-2020到1-20-2020的电话号码,那么我不希望其他客户在2020年1月27日之前预订此电话号码。我希望电话号码有一个为期7天的清晰窗口。

我有一个包含电话号码的表和一个包含通过phone_number_id与电话号码表相关的订单的表。订单表中有当前客户的旅行开始日期和结束日期,但尚未保存电话号码ID。订单表还具有所有其他客户旅行日期的开始日期和结束日期,以及为其旅行日期分配/预订的phone_number_id。

尝试选择当前客户日期可用的电话号码时,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逻辑。

任何帮助将不胜感激!预先感谢。

mysql left-join multiple-conditions
3个回答
2
投票

我解决问题的方法是从概念上看,是所有电话号码集和预定时间范围的叉积,然后排除那些有冲突的预定。

冲突将是重叠的,现有保留,该保留具有建议保留的start_date 之前 结束,并且具有[[开始]的end_date on or after >建议的保留。我要做一个反连接模式,像这样:

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_number行。 

我们可以将<测试设为<=或,减去8天,以便在此之前剪裁“ 7天”窗口;我们可以在测试案例中运行查询时进行调整,

我们可以使用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 )


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

例如

How to check if two date ranges overlap in mysql?

PHP/SQL - How can I check if a date user input is in between an existing date range in my database?

MySQL query to select distinct rows based on date range overlapping


编辑

基于对问题的编辑添加的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

两个“棘手”部分。首先是反连接,了解其工作原理。 (外部联接,从vir_num_table返回所有行,但排除保留行中具有匹配行的任何行。第二个棘手的部分是检查重叠情况,并提出条件:r.start <= p.end AND r.end> = p.start,然后调整是否要将相等值包含为重叠项,并调整额外的7天(对我来说,最简单的方法是从建议的预留开始之日减去7天)]

...现在发生在我身上,就像我们需要在预订期结束时增加7天的保护期,do!

根据您添加的信息,您无需检查已预订的电话号码的开始日期。

    您的客户为您提供开始日期和结束日期。
  • 您仅在电话最后一次租借结束后7天将电话号码租出
  • 您需要做的就是取回以下电话号码:-没有出租,因此不在订单表中-或者将结束日期设为新客户开始日期的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

    这里是一个查询加上排序算法,以选择最佳的电话号码选择,以实现最大的利用效率(即在每次使用前后,尽可能精确地接近7天)。

    我将其设置为开放末端的权重为9,因此将在开放末端数字之前选择“接近完美”的拟合值(之前或之后7-8天)。由于开放号码可以容纳任何预订,因此效率会略有提高。您可以根据需要进行调整。例如,如果将其设置为0,它将始终始终选择打开数字。

    SELECT ph.phone_number, COALESCE(MIN(DATEDIFF('2020-01-11', res.end_date)), 9) AS open_days_before, COALESCE(MIN(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天之前和新订单开始之间没有保留的电话号码视为“前端开放”


    1
    投票
    根据您添加的信息,您无需检查已预订的电话号码的开始日期。

    1
    投票
    这里是一个查询加上排序算法,以选择最佳的电话号码选择,以实现最大的利用效率(即在每次使用前后,尽可能精确地接近7天)。
    © www.soinside.com 2019 - 2024. All rights reserved.