Не получается правильный вывод через рекурсивную процедуру - PullRequest
0 голосов
/ 19 сентября 2019

У меня есть таблица с именем 'test_table'.У него есть два столбца с именами «parent» и «child», как показано ниже: Предполагается, что оба столбца имеют тип 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 голосов
/ 20 сентября 2019

Не рекурсивно вызывать процедуру;вместо этого используйте один иерархический запрос:

Процедура :

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 <> fiddle здесь

...