根据日期范围查找至少 2 个连续项目

Posted

技术标签:

【中文标题】根据日期范围查找至少 2 个连续项目【英文标题】:find at least 2 consecutive items based on date ranges 【发布时间】:2020-10-11 13:27:03 【问题描述】:

有很多类似问题的解决方案,但仅基于一个日期列。

我想知道可能有更好的解决方案来解决这个问题,我附上了我的解决方案,但如果您知道更好的解决方案,我发现它有点复杂,请发布。

这是包含 2 个项目的开始和结束日期的订单的表格。 我想根据日期和项目打印至少 2 个连续的行。

   ITEM , START , END
1. A, 01.01.2020, 31.01.2020
2. A,   01.02.2020, 31.03.2020
3. B,   01.02.2020, 30.04.2020
4. A,   01.05.2020, 30.06.2020
5. B,   01.06.2020, 31.07.2020
6. B,   01.09.2020, 30.09.2020
7. A,   01.08.2020, 31.10.2020
8. B,   01.10.2020, 31.10.2020
9. B,   01.11.2020, 31.12.2020

项目 A 的输出应该是第 1 行和第 2 行,项目 B 的输出应该是第 6,8 和 9 行


这是我的方法

with pool as (
                    select ITEM, START_DATE, END_DATE,
                              nvl(lag(end_date,1) over (partition by item order by end_date),START_DATE-1) prev_End_Date                    
                    from orders  )
, pool2 as     (
                    select item ,
                              START_DATE, END_DATE,
                              sum(case when PREV_END_DATE+1 = START_DATE then 0 else 1 end ) over (partition by item order by START_DATE) grp
                              from pool )
select item,start_date,end_date from (
select 
          ITEM, 
          START_DATE,
          END_DATE,
          grp,
          count(grp) over (partition by item,grp ) cnt
 from pool2)
 where cnt>=2
 ;

【问题讨论】:

【参考方案1】:

嗯。 . .使用 lag()lead() 查看下一个/上一个值并检查它们是否匹配:

select o.*
from (select o.*,
             lag(end) over (partition by product order by start) as prev_end,
             lead(start) over (partition by product order by start) as next_start
      from orders o
     ) o
where start = prev_end + interval '1' day or
      end = next_start - interval '1' day;

【讨论】:

【参考方案2】:

-- 为测试创建表并插入行

Create table order_overlap (id number, item varchar2(1), start_date date , end_date date );
 

insert into order_overlap(id,start_date, end_date, item) values( 1,to_date('01.01.2020', 'dd.mm.yyyy'), to_date( '31.01.2020', 'dd.mm.yyyy'), 'A');
insert into order_overlap(id,start_date, end_date, item) values( 2, to_date('01.02.2020', 'dd.mm.yyyy'), to_date( '31.03.2020', 'dd.mm.yyyy'), 'A');
insert into order_overlap(id,start_date, end_date, item) values( 3, to_date('01.02.2020', 'dd.mm.yyyy'), to_date( '30.04.2020', 'dd.mm.yyyy'), 'B');
insert into order_overlap(id,start_date, end_date, item) values( 4, to_date('01.05.2020', 'dd.mm.yyyy'), to_date( '30.06.2020', 'dd.mm.yyyy'), 'A');
insert into order_overlap(id,start_date, end_date, item) values( 5, to_date('01.06.2020', 'dd.mm.yyyy'), to_date( '31.07.2020', 'dd.mm.yyyy'), 'B');
insert into order_overlap(id,start_date, end_date, item) values( 6, to_date('01.09.2020', 'dd.mm.yyyy'), to_date( '30.09.2020', 'dd.mm.yyyy'), 'B');
insert into order_overlap(id,start_date, end_date, item) values( 7, to_date('01.08.2020', 'dd.mm.yyyy'), to_date( '31.10.2020', 'dd.mm.yyyy'), 'A');
insert into order_overlap(id,start_date, end_date, item) values( 8, to_date('01.10.2020', 'dd.mm.yyyy'), to_date( '31.10.2020', 'dd.mm.yyyy'), 'B');
insert into order_overlap(id,start_date, end_date, item) values( 5, to_date('01.11.2020', 'dd.mm.yyyy'), to_date( '31.12.2020', 'dd.mm.yyyy'), 'B');

-- 我做了一些不同的事情,但也许你喜欢它。 - 我将连续的行合并为一个 - 所以如果你有项目 A 01.01.2020 - 31.01.2020 A 01.02.2020 - 28.02.2020

你会得到一份记录 A 01.01.2020 - 28.02.2020

 select item, min(start_date) start_date , max(end_date) end_date, count(*)  
 from (  
   select item, start_date, end_date,  
     case when lead(start_date) over(partition by item order by start_date) = end_date + 1   
      OR lag(end_date) over(partition by item order by end_date) + 1 = start_date   
      then 0  
      else rownum  
     end continuity  
   from order_overlap ) 
 group by item, continuity
 order by item, start_date;  

【讨论】:

是的,这是另一种类型的输出,谢谢,但是如果我想要精确的 2 个连续行,那么您将如何排除项目 B 记录,以便只有项目 A 会在输出中【参考方案3】:

您可以简单地使用MATCH_RECOGNIZE 执行逐行比较,并仅返回与模式匹配的行组:

SELECT *
FROM   table_name
MATCH_RECOGNIZE (
  PARTITION BY item
  ORDER BY start_date, end_date
  ALL ROWS PER MATCH
  PATTERN ( FIRST_ROW NEXT_ROWS+ )
  DEFINE
    NEXT_ROWS AS (
      NEXT_ROWS.START_DATE = PREV( END_DATE ) + INTERVAL '1' DAY
    )
)

因此,对于您的示例数据:

CREATE TABLE table_name ( ITEM, START_DATE, END_DATE ) AS
SELECT 'A', DATE '2020-01-01', DATE '2020-01-31' FROM DUAL UNION ALL
SELECT 'A', DATE '2020-02-01', DATE '2020-03-31' FROM DUAL UNION ALL
SELECT 'B', DATE '2020-02-01', DATE '2020-04-30' FROM DUAL UNION ALL
SELECT 'A', DATE '2020-05-01', DATE '2020-06-30' FROM DUAL UNION ALL
SELECT 'B', DATE '2020-06-01', DATE '2020-07-31' FROM DUAL UNION ALL
SELECT 'B', DATE '2020-09-01', DATE '2020-09-30' FROM DUAL UNION ALL
SELECT 'A', DATE '2020-08-01', DATE '2020-10-31' FROM DUAL UNION ALL
SELECT 'B', DATE '2020-10-01', DATE '2020-10-31' FROM DUAL UNION ALL
SELECT 'B', DATE '2020-11-01', DATE '2020-12-31' FROM DUAL;

这个输出:

项目 | START_DATE |结束日期 :--- | :--------- | :--------- 一个 | 2020-01-01 | 2020-01-31 一个 | 2020-02-01 | 2020-03-31 乙| 2020-09-01 | 2020-09-30 乙| 2020-10-01 | 2020-10-31 乙| 2020-11-01 | 2020-12-31

db小提琴here

【讨论】:

用我的方法我有能力选择连续行的数量,假设我想要精确的 2 个连续行,所以项目 B 不会出现在输出中,你将如何实现这一点match_recognize? @PatrikMelichercik 这不是问题中的要求之一;但是,将MEASURES 子句添加到COUNT 组的大小然后过滤该db<>fiddle 相对简单。

以上是关于根据日期范围查找至少 2 个连续项目的主要内容,如果未能解决你的问题,请参考以下文章

在 db2 中查找不具有连续日期范围的行

EXCEL问题:如何实现根据输入数值动态显示想要显示的数据范围的大小?

如何查找两个日期之间的连续天数

根据日期范围查找记录

hutool日期工具类相关:获取某月所有周某周的起止时间或所有日期计算连续天数

根据列查找日期范围