调整组内的日期重叠

adjust date overlaps within a group

我有这个 table 我想在下一个 ST_DATE 前一天调整 END_DATE 以防一组 ID

有重叠日期]
TABLE HAVE

ID   ST_DATE         END_DATE
1   2020-01-01     2020-02-01
1   2020-05-10     2020-05-20
1   2020-05-18     2020-06-19
1   2020-11-11     2020-12-01  
2   1999-03-09     1999-05-10
2   1999-04-09     2000-05-10
3   1999-04-09     2000-05-10
3   2000-06-09     2000-08-16
3   2000-08-17     2009-02-17

以下是我要查找的内容

TABLE WANT

ID   ST_DATE         END_DATE
1   2020-01-01     2020-02-01
1   2020-05-10     2020-05-17  =====changed to a day less than the next ST_DATE due to some sort of overlap
1   2020-05-18     2020-06-19
1   2020-11-11     2020-12-01  
2   1999-03-09     1999-04-08 =====changed to a day less than the next ST_DATE due to some sort of overlap
2   1999-04-09     2000-05-10
3   1999-04-09     2000-05-10
3   2000-06-09     2000-08-16
3   2000-08-17     2009-02-17




或许您可以使用 LEAD() 来解决这个问题。初步想法:

select 
  id, st_date, end_date
, lead( st_date ) over ( partition by id order by st_date ) nextstart_
from overlap
;

-- result
        ID ST_DATE   END_DATE  NEXTSTART
---------- --------- --------- ---------
         1 01-JAN-20 01-FEB-20 10-MAY-20
         1 10-MAY-20 20-MAY-20 18-MAY-20
         1 18-MAY-20 19-JUN-20 11-NOV-20
         1 11-NOV-20 01-DEC-20          
         2 09-MAR-99 10-MAY-99 09-APR-99
         2 09-APR-99 10-MAY-00          
         3 09-APR-99 10-MAY-00 09-JUN-00
         3 09-JUN-00 16-AUG-00 17-AUG-00
         3 17-AUG-00 17-FEB-09 

一旦你有了下一个开始日期和 end_date 并排(可以说), 您可以使用 CASE ... 根据需要调整日期。

select ilv.id, ilv.st_date
, case 
    when ilv.end_date > ilv.nextstart_ then
      to_char( ilv.nextstart_ - 1 ) || ' <- modified end date'
    else
      to_char( ilv.end_date )
  end dt_modified
from (
  select 
    id, st_date, end_date
  , lead( st_date ) over ( partition by id order by st_date ) nextstart_
  from overlap
) ilv
;

        ID ST_DATE   DT_MODIFIED                            
---------- --------- ---------------------------------------
         1 01-JAN-20 01-FEB-20                              
         1 10-MAY-20 17-MAY-20 <- modified end date         
         1 18-MAY-20 19-JUN-20                              
         1 11-NOV-20 01-DEC-20                              
         2 09-MAR-99 08-APR-99 <- modified end date         
         2 09-APR-99 10-MAY-00                              
         3 09-APR-99 10-MAY-00                              
         3 09-JUN-00 16-AUG-00                              
         3 17-AUG-00 17-FEB-09    

DBfiddle 这里

如果同一个 id 的两个 "windows" 具有相同的开始日期,那么这个问题就没有意义了。所以,让我们假设这个问题是有道理的——也就是说,组合 (id, st_date) 在输入中是唯一的。

然后,问题可以表述如下:对于每个 id,按 st_date 升序排列行。然后,对于每一行,如果它的 end_dt 小于下面的 st_date,则 return 该行保持原样。否则将 end_dt 替换为以下 st_date,减 1。这最后一步可以通过解析 lead() 函数来实现。

解决方案可能如下所示:

select id, st_date,
       least(end_date, lead(st_date, 1, end_date + 1) 
                       over (partition by id order by st_date) - 1) as end_date
from   have
;

lead 函数中关于 end_date + 1 的位处理每个 id 的最后一行。对于这样的行,没有 "next" 行,因此 lead 的默认应用将 return null。可以使用函数的第三个参数覆盖默认值。