清理重叠有效期从 -

时间:2017-02-01 16:48:07

标签: sql oracle oracle12c

考虑一个随时间跟踪喜欢的颜色的表格。

drop table favourites;

create table favourites(
   person_id   varchar2(10) not null
  ,valid_from  date         not null
  ,valid_to    date
  ,color       varchar2(10) not null
  ,constraint favourites_pk primary key(person_id, valid_from)
);

insert into favourites values('Ronnie', date '1979-09-12',  null,              'Green');
insert into favourites values('Ronnie', date '2000-01-01',  date '2016-12-31', 'Blue');

commit;

该表列出了' Ronnie'喜欢绿色从1979-09-12然后从2000-01-01他喜欢蓝色。当Ronnie在2017年的第一天醒来时,他不再喜欢Blue了。

我需要用一次性脚本清理一张桌子,但我仍然坚持上面显示的特定情况。到目前为止,最好的想法是根据valid_from日期重新计算valid_to日期,但在这种情况下,我实际上会破坏信息:如下所示" green"不再是2017-01-01的最爱,它应该是。

select person_id
      ,valid_from      
      ,valid_to
      ,lead(valid_from,1) over(partition by person_id order by valid_from)-1 as valid_to2
      ,color        
  from favourites t;


PERSON_ID   VALID_FROM  VALID_TO    NEW_VALID_TO   COLOR
---------   ----------  ----------  ------------   ------
Ronnie      1979-09-12  null        1999-12-31     Green
Ronnie      2000-01-01  2016-12-31  null           Blue

如何生成aditional记录,以便最终结果为:

PERSON_ID   VALID_FROM  VALID_TO    COLOR
---------   ----------  ----------  ------
Ronnie      1979-09-12  1999-12-31  Green
Ronnie      2000-01-01  2016-12-31  Blue
Ronnie      2017-01-01  null        Green

修改 valid_to日期背后没有合理的逻辑。这是用户所投入的内容,因此存在各种疯狂的重叠。 null表示" forever"或9999-12-31,如果它更容易理解。我想通过创建另一个表来修复此设计,但首先我需要一种方法来修复旧数据。

也许以这种方式看待它会更容易。

Green |---------------------------------->
Blue            |------| 

我通过计算新的valid_to日期修复重叠,我销毁信息:

Green |--------|
Blue            |------| 

必须像这样修复:

Green |--------|
Blue            |------| 
Green                   |-------->

原始数据集中的问题仅显示第二个查询(由于重叠)。另外两个显示正确的结果。

select *
  from favourites 
 where (date '1995-01-01' >= valid_from)
   and (date '1995-01-01' <= valid_to or valid_to is null);

select * 
  from favourites 
 where (date '2015-01-01' >= valid_from)
   and (date '2015-01-01' <= valid_to or valid_to is null);   

select * 
  from favourites 
 where (date '2025-01-01' >= valid_from)
   and (date '2025-01-01' <= valid_to or valid_to is null);

2 个答案:

答案 0 :(得分:0)

如果您的示例是您关注的一种重叠类型,那么您可以使用union all处理此问题。我想这是你想要的查询:

select person_id, color, valid_from,
       coalesce(valid_to, lead(valid_from) over (partition by person_id order by valid_from)-1) as valid_to2
from favourites t
union all
select person_id, color, valid_to + 1 as valid_from, NULL as valid_to
from favourites f
where valid_from is null and
      exists (select 1 from favourites f2 where f2.person_id = f.person_id and f2.valid_from > f.valid_from);

作为唯一的类型,我的意思是只有一个valid_fromNULL,并且记录不会重叠。如果您有更多生成问题,那么您应该询问另一个问题,并提供适当的数据和解释如何处理重叠。

答案 1 :(得分:0)

获取当前行的结束日期很简单(从下一行减去1天&#39; date_from)。但是,您应该生成一个需要union all的新行。此外,默认颜色选择为valid_tonull的行中的颜色。 (如果不是这样的话,你需要改变cte中的逻辑)。

WITH CTE AS
 (SELECT PERSON_ID,
         COLOR,
         VALID_FROM,
         ROW_NUMBER() OVER(PARTITION BY PERSON_ID ORDER BY VALID_FROM DESC) AS RNUM,
         COALESCE(VALID_TO, LEAD(VALID_FROM) OVER (PARTITION BY PERSON_ID
                                                   ORDER BY VALID_FROM)-1) AS VALID_TO_NEW,
         MAX(CASE WHEN VALID_TO IS NULL THEN COLOR END) 
                  OVER(PARTITION BY PERSON_ID ORDER BY VALID_FROM) AS DEFAULT_COLOR
  FROM FAVOURITES)
SELECT PERSON_ID,
       VALID_FROM,
       VALID_TO_NEW,
       COLOR
FROM CTE
UNION ALL
--Generates a new last row with the default color if it exists
SELECT PERSON_ID,
       VALID_TO_NEW+1,
       NULL,
       DEFAULT_COLOR
FROM CTE 
WHERE RNUM=1

Sample Demo