【问题标题】:clean up overlapping Valid from - valid to清理重叠的有效从 - 有效到
【发布时间】:2017-02-01 16:48:07
【问题描述】:

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

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 年 9 月 12 日开始喜欢绿色,然后从 2000 年 1 月 1 日开始他喜欢蓝色。当罗尼在 2017 年的第一天醒来时,他不再喜欢 Blue。

我需要使用一次性脚本清理表格,但我遇到了上面显示的特定情况。到目前为止,最好的想法是根据 valid_from 日期重新计算 valid_to 日期,但在这种情况下,我实际上破坏了信息:如下所示,“绿色”不再是 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

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

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 表示“永远”,或者 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);

【问题讨论】:

  • 这是唯一的重叠示例吗?
  • 为什么新插入的行的颜色应该是绿色的?可以为空吗?
  • 你怎么知道他在 2017 年又开始喜欢绿色了?某处有默认规则吗?
  • vkp 在上述评论中的建议是最有意义的。表格中的数据告诉您,Ronnie 喜欢 Blue 到 2016 年底。如果您没有其他信息,那么从那时起他喜欢什么就不得而知了,null 正是为此。我们怎么知道他不再喜欢蓝色,更不用说绿色或蓝色以外的颜色了? (或者您是否有其他未分享的条件,例如喜欢的颜色不可为空,只有两个可能的值,并且颜色必须从一行到下一行交替出现?)
  • @GordonLinoff,不,还有更多类型的重叠,但我已经解决了。

标签: sql oracle oracle12c


【解决方案1】:

如果您的示例是您关心的一种重叠类型,那么您可以使用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,并且记录不会重叠。如果您有更多生成的问题,那么您应该提出另一个问题,并提供适当的数据并解释如何处理重叠。

【讨论】:

    【解决方案2】:

    获取当前行的结束日期很容易(从下一行的 date_from 中减去 1 天)。但是,您应该生成一个需要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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-09-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-15
      • 2021-12-16
      • 2023-01-26
      相关资源
      最近更新 更多