PL / SQL中的递归

时间:2014-02-27 07:17:06

标签: sql oracle plsql combinations

我的任务是在n组中组合元素。这样一组中没有任何元素与自身结合。结构是:

  

产品--->控制---> LOV [值列表]

     

示例场景:

     

1产品 - >具有3个对照控制1 - >具有3个LOV控制2 - >   具有2个LOV控制3 - >有5个LOV

     

没有。产品1的可能组合:3 * 2 * 5 = 30。

作为一名程序员 - 在SQL中的一个菜鸟 - 我立即使用递归。我不知道PL / SQL中递归的效率。但是我通过遍历所有控件(如树)并在树叶处拾取值来获得所需的结果。解决方案有效,但如果没有递归就可以做到可能的方式吗?

procedure postcombinations(pProductID in varchar2,
                           lovs       in varchar2,
                           ctrl       in varchar2) is
  lv_cp varchar2(100);
  lv_cl varchar2(100);
begin

  -- Loop through All the controls defined against a product other
  -- than the ones already traversed (Not in condition) also 
  -- restrict results to One branch since order is doesn't matter.

  for i in (select cp.control_id
              from tbl_control_product cp, tbl_control c
             where cp.product_id = pProductID
               and cp.control_id = c.control_id
               and c.control_type = 2
               and cp.control_id not in
                   (select regexp_substr(ctrl, '[^,]+', 1, level)
                      from dual
                    connect by regexp_substr(ctrl, '[^,]+', 1, level) is not null)
               and rownum < 2) loop
    begin

      -- Loop through all the LOV's in the controls and append them to Input 
      --to recurse the function 
      for p in (select cl.lov_id
                  from tbl_control_lov cl
                 where cl.control_id = i.control_id) loop
        if lovs is not null then
          pkg_product.postcombinations(pProductID => pProductID,
                                       lovs       => lovs || ',' ||
                                                     p.lov_id,
                                       ctrl       => ctrl || ',' ||
                                                     i.control_id);
        else
          pkg_product.postcombinations(pProductID => pProductID,
                                       lovs       => lovs,
                                       ctrl       => ctrl || ',' ||
                                                     i.control_id);
        end if;

      end loop;
    end;
  end loop;

  -- When A leaf is encountered the select statement returns a null 
  --the inputs are dumped into a table and voila.
  begin
    select cp.control_id
      into lv_cp

      from tbl_control_product cp, tbl_control tc
     where cp.product_id = pProductID
       and cp.control_id = tc.control_id
       and tc.control_type = 2
       and cp.control_id not in
           (select regexp_substr(ctrl, '[^,]+', 1, level)
              from dual
            connect by regexp_substr(ctrl, '[^,]+', 1, level) is not null)
       and rownum < 2;
  Exception
    When NO_DATA_FOUND then
      insert into tbl_test values (ctrl, lovs);
      commit;
  end;

end;

1 个答案:

答案 0 :(得分:1)

目前还不完全清楚您正在做什么,但这会为您提供单个产品的control_idlov_id的30种组合:

select tcp.control_id, tcl.lov_id
from tbl_control_product tcp
join tbl_control tc on tc.control_id = tcp.control_id
cross join tbl_control_lov tcl
where tcp.product_id = <productID>
and tc.control_type = 2;

三个控件中的每一个都可以看到其中任何一个的10个LOV。

但是根据我认为你的程序正在做的事情,似乎如果产品有三个控件,你想列出这些控件,以及它们下面的所有LOV组合。您的程序似乎在else中有错误 - 我认为您需要lovs => p.lov_id而不是lovs => lovs;通过该更改,初始呼叫可以具有lovs => null。但是您似乎必须传递ctrls的初始数字,这会破坏输出。如果我已经按照它并正确创建数据(请参阅下面的小提琴),那么如果调用pkg_product.postcombinations(pProductID => 'ABC', lovs => null, ctrl => '0'),您最终会插入类似的内容:

ctrl 0,11,12,13 lovs 101,201,301
ctrl 0,11,12,13 lovs 101,201,302
ctrl 0,11,12,13 lovs 101,201,303
...
ctrl 0,11,12,13 lovs 103,202,304
ctrl 0,11,12,13 lovs 103,202,305

如果这是正确的,那么你可以用一个SQL语句做同样的事情,只要你使用11gR2,因为它使用递归子查询因子分析:

with t as (
  select tcp.control_id, tcl.lov_id,
    dense_rank() over (partition by tcp.product_id
      order by tcp.control_id) as control_num,
    count(distinct tcp.control_id)
      over (partition by tcp.product_id) as control_count
  from tbl_control_product tcp
  join tbl_control tc on tc.control_id = tcp.control_id
  join tbl_control_lov tcl on tcl.control_id = tc.control_id
  where tcp.product_id = 'ABC'
  and tc.control_type = 2
),
r (control_num, control_count, ctrl, lovs) as (
  select control_num, control_count, to_char(control_id), to_char(lov_id)
  from t
  where control_num = 1
  union all
  select t.control_num, t.control_count,
    ctrl ||','|| control_id, lovs ||','|| lov_id
  from r
  join t on t.control_num = r.control_num + 1
)
select ctrl, lovs
from r
where control_num = control_count
order by ctrl, lovs;

这与你正在使用的逻辑非常相似,有点儿。这给了:

CTRL                 LOVS               
-------------------- --------------------
11,12,13             101,201,301          
11,12,13             101,201,302          
11,12,13             101,201,303          
...
11,12,13             103,202,304          
11,12,13             103,202,305  

SQL Fiddle