Oracle SQL:如何消除CTE中的冗余递归调用

时间:2017-04-09 11:03:00

标签: sql oracle recursion

以下设置代表产品连续几周的销售额。

22,19,20,23,16,14,15,15,18,21,24,10,17

...     每周销售表

date        sales
week-1 :    22
week-2 :    19 
week-3 :    20 
... 
week-12 :   10
week-13 :   17

我需要连续几周找到最长的高销售数字,即由14,15,15,18,21,24代表的第6周到第11周。

我正在尝试使用递归CTE前进到下周,以查找销售额是否相等或更高。只要值相等或更高,继续移动到下一周,记录锚成员的ROWNUMBER(表示起始周数)和迭代行的周数。使用这种方法,有冗余的递归调用。例如,当第2周调用cte时,它会迭代第3周,第4周和第5周,因为销售价值比前一周的每周更高。现在,在第2周之后,应该在第5周调用cte,因为已经访问了第3周,第4周和第5周。

基本上,如果我已经在递归调用中访问了一行filt_coll,我不希望它再次传递给CTE。标识为冗余的行不应找到,actualweek列的值应该是唯一的。

我知道下面的sql没有解决我找到最长的较高值的问题。我可以从startweek列的最大数量计算出来。目前,我正在试图找出如何消除冗余递归调用。

START_WEEK | SALES | SALESLAG | SALESLEAD | ACTUALWEEK    
1          | 22   | 0     | -3            | 1
2          | 19   | -3    | 1             | 2
2          | 20   | 1     | 3             | 3
2          | 23   | 3     | -7            | 4
3          | 20   | 1     | 3             | 3 <-(redundant)
3          | 23   | 3     | -7            | 4 <-(redundant)
4          | 23   | 3     | -7            | 4 <-(redundant)
6          | 14   | -2    | 1             | 6

...

with 
-- begin test data 
raw_data (sales) as
(
  select '22,19,20,23,16,14,15,15,18,21,24,10,17' from dual
)
,
derived_tbl(week, sales) as
(
  select level, regexp_substr(sales, '([[:digit:]]+)(,|$)', 1, level, null, 1)
  from raw_data connect by level <= regexp_count(sales,',')+1
)
-- end test data
,
coll(week, sales, saleslag, saleslead) as
(
  select week, sales, 
    nvl(sales - (lag(sales) over (order by week)), 0), 
    nvl((lead(sales) over (order by week) - sales), 0)
  from derived_tbl
)
,
filt_coll(week, sales, saleslag, saleslead) as
(
  select week, sales, saleslag, saleslead 
  from coll 
  where not (saleslag < 0 and saleslead < 0)
)
,
cte(startweek, sales, saleslag, saleslead, actualweek) as
(
  select week, sales, saleslag, saleslead, week from filt_coll 
  -- where week not in (select week from cte)
  -- *** want to achieve the effect of the above commented out line
  union all
  select cte.startweek, cl.sales, cl.saleslag, cl.saleslead, cl.week 
  from filt_coll cl, cte
  where cl.week = cte.actualweek + 1 and cl.sales >= cte.sales
)
select * from cte 
order by 1,actualweek
;

0 个答案:

没有答案