0

我有一个可以通过递归 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 来做到这一点,但性能很糟糕(对于一个相对较小的数据集,它会产生 3100 万行的最终表,需要 20-25 分钟):

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
;
4

1 回答 1

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
...

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

于 2017-03-28T16:20:01.107 回答