无法通过递归过程获得正确的输出

时间:2019-09-19 20:40:26

标签: oracle stored-procedures plsql

我有一个名为“ test_table”的表。它有两个名为“父母”和“孩子”的栏,如下所示:     假设两列都是Varchar2类型。

    PARENT    CHILD
    ---------------------
    50-100    10-001
    50-100    10-002
    50-100    10-003
    50-100    11-100
    50-100    11-101
    11-100    10-100
    11-100    10-101
    11-100    10-102

我需要编写一种递归过程,如果给定父母的孩子以“ 10”开头,那么它将被打印出来。 如果子值以“ 11”开头,则将为每个以11开头的子值递归调用该过程。 下面是所需的示例输出:

CHILD
--------------
10-001
10-002
10-003
10-100
10-101
10-102

这是示例过程:

create or replace type type_test2 as table of varchar2(2000);

create or replace procedure test_proc
     (parent_data in type_test2,child_data out type_test2)
as 
    v_out type_test2:=type_test2();
    begin
      child_data:=type_test2();
      select child 
      bulk collect into v_out 
      from test_table 
      where parent in(select * from table(parent_data));

      for i in 1..v_out.count loop

        child_data.extend(i);
        v_out.extend(i);

        if v_out(i) like '10-%' then
           child_data(i):=v_out(I);
        elsif v_out(i) like '11-%' then
           test_proc(v_out,child_data);
        end if;

    end loop;
 end;
/

执行上述过程:

    declare
        a type_test2:=type_test2('50-100');
        b type_test2:=type_test2();
    begin
        test_proc(a,b);
        for i in 1..b.count loop
            dbms_output.put_line(b(I));
        end loop;
    end;
 /

但是我只得到5个子值,而不是6个值。缺少“ 10-100”。

        Child
        ---------------
        10-102
        10-101
        10-003
        10-002
        10-001

请帮助我获得正确的输出。

1 个答案:

答案 0 :(得分:0)

不要递归调用该过程;而是使用单个分层查询:

过程

CREATE PROCEDURE test_proc (
  i_parent   IN  test_table.parent%TYPE,
  o_children OUT type_test2
)
AS
BEGIN
  SELECT CHILD
  BULK COLLECT INTO o_children
  FROM   test_table
  WHERE  CHILD like '10-%'
  START WITH parent = i_parent
  CONNECT BY NOCYCLE
         PRIOR child = parent
  AND    PRIOR child LIKE '11-%';
END;
/

执行

DECLARE
  b type_test2:=type_test2();
BEGIN
  test_proc( '50-100', b );
  FOR i IN 1..b.COUNT LOOP
    DBMS_OUTPUT.PUT_LINE( b(i) );
  END LOOP;
END;
 /

输出

10-001
10-002
10-003
10-100
10-101
10-102

备用

您甚至不需要一个过程,并且可以在一个查询中完成所有操作:

SELECT CHILD
FROM   test_table
WHERE  CHILD like '10-%'
START WITH parent = '50-100'
CONNECT BY NOCYCLE
       PRIOR child = parent
AND    PRIOR child LIKE '11-%';

哪个输出:

| CHILD  |
| :----- |
| 10-001 |
| 10-002 |
| 10-003 |
| 10-100 |
| 10-101 |
| 10-102 |

  

为什么递归过程没有得到正确的输出。

每次使用BULK COLLECT INTO时,它都会重新初始化要输出到的集合并覆盖以前的值。您需要BULK COLLECT INTO一个不同的集合,然后如果想要保留以前的值,则将两者合并。

类似的东西:

CREATE OR REPLACE PROCEDURE test_proc(
  parent_data IN  type_test2,
  child_data  OUT type_test2
)
AS 
  v_to_search      type_test2;
  v_search_results type_test2;
  i PLS_INTEGER;
BEGIN
  SELECT child
  BULK COLLECT INTO child_data 
  FROM   test_table 
  WHERE  parent MEMBER OF parent_data
  AND    child LIKE '10-%';

  SELECT child
  BULK COLLECT INTO v_to_search 
  FROM   test_table 
  WHERE  parent MEMBER OF parent_data
  AND    child LIKE '11-%';

  IF v_to_search.COUNT = 0 THEN
    RETURN;
  END IF;
  test_proc( v_to_search, v_search_results );
  i := child_data.COUNT;
  child_data.EXTEND( v_search_results.COUNT );
  FOR j IN 1 .. v_search_results.COUNT LOOP
    child_data(i+j) := v_search_results(j);
  END LOOP;
END;
/

但是,如果您的数据中存在一个循环,则可能陷入无限循环。上面的分层查询将检测循环并停止迭代(或者,如果您删除NOCYCLE关键字,则会引发异常)。

db <>提琴here