【问题标题】:find overlap between two sets of date ranges查找两组日期范围之间的重叠
【发布时间】:2021-06-19 08:40:19
【问题描述】:

我想使用 Oracle SQL 或 PL/SQL 查找每个订单号的两组日期范围之间的重叠。

输入是“结果集一”和“结果集二”。输出应该是“重叠”的。

结果集一

WITH T_RESULT_SET_ONE as(
select 21365 order_number,to_date('01/01/2021 09:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 10:30:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21365 order_number,to_date('02/01/2021 14:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('02/01/2021 18:00:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21367 order_number,to_date('01/01/2021 08:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 09:43:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21367 order_number,to_date('01/01/2021 16:34:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 18:15:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
union
select 21367 order_number,to_date('04/01/2021 15:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('04/01/2021 16:15:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
)

结果集二

T_RESULT_SET_TWO as(
select 21365 order_number,to_date('01/01/2021 09:30:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 09:45:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21365 order_number,to_date('02/01/2021 13:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('02/01/2021 17:00:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
union
select 21367 order_number,to_date('01/01/2021 09:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 10:00:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21367 order_number,to_date('01/01/2021 16:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 19:00:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21367 order_number,to_date('05/01/2021 19:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('04/01/2021 19:46:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
)

重叠

T_OVERLAP as
(
select 21365 order_number,to_date('01/01/2021 09:30:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 09:45:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
union
select 21365 order_number,to_date('02/01/2021 14:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('02/01/2021 17:00:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
union
select 21367 order_number,to_date('01/01/2021 09:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 09:43:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21367 order_number,to_date('01/01/2021 16:34:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 18:15:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
)

下图说明了我正在尝试执行的操作(日期范围与我之前提供的不同)

谁能提供一个 SQL 查询或 PL/SQL 程序来做到这一点?

【问题讨论】:

  • 从输入集到“交叉点”的规则是什么?显然,它不仅仅是 ONE INTERSECT TWO,因为它会生成一个空数据集。 START 和 FINISH 日期呢?乍一看,我没有看到这种模式(也不想考虑它),所以 - 也许你想解释一下。
  • @Littlefoot,我用图表更新了帖子,希望这将使模式的可视化更容易。

标签: sql oracle plsql oracle11g date-range


【解决方案1】:

您正在寻找的似乎不是 INTERSECT 而是重叠。在Oracle中相交一般是指2个查询的共同结果:

Select <columns list> from table1
INTERSECT
Select <columns list> from table2;

列列表具有相同的定义并且结果值相同。您正在寻找的是值彼此重叠的位置,而不是行包含相同值的位置。
让我们考虑 2 个事件调用,然后是 'A' 和 'B',重叠的可能性有 4 种:

  1. A 开始,B 开始,B 结束,A 结束。 A 与 B 完全重叠。
  2. A 开始,B 开始,A 结束,B 结束。 A 与 B 的开头重叠
  3. B 开始,A 开始,B 结束,A 结束。 B的A重叠结尾
  4. B 开始,A 开始,A 结束,B 结束。 A 与 B 完全重叠。

解决就是确定需要确定我们取最大开始时间和最小结束时间的重叠。使用您提供的数据,这仅需要上述一项:

select order_number
     , greatest(t1start, t2start) start_date_time
     , least(t1finish,t2finish) finish_date_time     
 from ( select t1.order_number
             , t1.start_date_time   t1start
             , t1.finish_date_time  t1finish
             , t2.start_date_time   t2start
             , t2.finish_date_time  t2finish             
          from t_result_set_one t1
          join t_result_set_two t2
            on t1.order_number = t2.order_number 
         where (    t1.finish_date_time >= t2.start_date_time
                and t1.start_date_time  <= t2.finish_date_time
               ) 
      );    

fiddle here。其他 3 种可能性我留给你。

【讨论】:

  • 您能否进一步解释一下“我将其他 3 种可能性留给您”的意思?在我看来,无论重叠模式如何,您提供的 sql 查询都有效。我的意思是,它似乎适用于可能性 1、2、3 和 4。
  • 显然你是对的,它似乎涵盖了所有 4 种情况。我没想到会这样。我希望每个都需要一个 OR 条件,以消除不重叠的条件。我想这应该教我,仅仅因为我从我不熟悉的样本数据中得到了意想不到的结果,我不应该假设它只涵盖 1 个案例并且需要其他案例的额外数据。 (不管多晚)