计算Oracle中日期范围的值(可能使用递归CTE)

时间:2017-03-28 15:55:52

标签: oracle performance date recursive-cte

我遇到的问题可以通过递归CTE处理,但不能在可接受的时间段内处理。任何人都可以指出我提高性能和/或以不同方式获得相同结果的方法吗?

这是我的情景!

我有:一个大表,每行包含一个id,一个开始日期,一个结束日期和一个排名编号。每个id有多行,日期范围经常重叠。日期从2010年开始。

我想:一个表,其中包含id + date的每个组合的行,该行位于上一个表中该id的任何日期范围内。每行应该具有该ID和日期的最低排名数。

例如:

ID  Rank  Range
1   1     1/1/2010-1/4/2010
1   2     1/2/2010-1/5/2010
2   1     1/1/2010-1/2/2010

变为

ID  Rank  Day
1   1     1/1/2010
1   1     1/2/2010
1   1     1/3/2010
1   1     1/4/2010
1   2     1/5/2010
2   1     1/1/2010
2   1     1/2/2010

我可以通过递归CTE来实现这一点,但性能非常糟糕(相对较小的数据集需要20-25分钟才能产生3100万行的最终表格):

with enc(PersonID, EncounterDate, EndDate, Type_Rank) as (
select PersonID, EncounterDate, EndDate, Type_Rank
from Big_Base_Table
union all
select PersonID, EncounterDate + 1, EndDate, Type_Rank
from enc
where EncounterDate + 1 <= EndDate
)
select PersonID, EncounterDate, min(Type_Rank) Type_Rank
from enc
group by PersonID, EncounterDate
;

1 个答案:

答案 0 :(得分:0)

您可以在CTE中从表格中提取所有可能的日期,然后将其加入表格中:

with all_dates (day) as (
  select start_date + level - 1
  from (
    select min(start_date) as start_date, max(end_date) as end_date
    from big_base_table
  )
  connect by level <= end_date - start_date + 1
)
select bbt.id, min(bbt.type_rank) as type_rank, to_char(ad.day, 'YYYY-MM-DD') as day
from all_dates ad
join big_base_table bbt
on bbt.start_date <= ad.day
and bbt.end_date >= ad.day
group by bbt.id, ad.day
order by bbt.id, ad.day;

        ID  TYPE_RANK DAY       
---------- ---------- ----------
         1          1 2010-01-01
         1          1 2010-01-02
         1          1 2010-01-03
         1          1 2010-01-04
         1          2 2010-01-05
         2          1 2010-01-01
         2          1 2010-01-02


7 rows selected. 

CTE从任何ID的最低日期获取所有日期,直至任何ID的最高日期。如果你有一个静态日历表,你也可以使用静态日历表来保存两次表(并且至少在某些版本中同时获得最小值/最大值)。

你也可以反过来写,如:

...
from big_base_table bbt
join all_dates ad
on ad.day >= bbt.start_date
and ad.day <= bbt.end_date
...

但我认为优化者可能最终会对它们进行相同的处理,只需对基表进行一次完整扫描;值得检查它实际上为两者提出的计划,如果一个比另一个更有效。