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

Я пишу одну рекурсивную процедуру для приведенной ниже таблицы (sample_table): Учтите, что каждый столбец имеет тип данных varchar2:

PARENT_DATA CHILD_DATA  PARENT_VERSION  CHILD_VERSION
----------------------------------------------------------------
20-10       40-01       -A11            -A11
20-10       40-02       -A11            -A11
20-10       40-03       -A11            -A11
20-10       80-10       -A11            -A11
20-10       81-10       -A11            -A11
80-10       40-100      -A11            -A11
80-10       40-101      -A11            -A11
80-10       40-102      -A11            -A11

Мне нужно написать рекурсивную процедуру, такую, что для данного PARENT_DATA, если CHILD_DATA запускаетсяс '40' тогда это будет напечатано.Если CHILD_DATA начинается с '80', то процедура будет вызываться рекурсивно.Окончательный результат будет таким, как показано ниже:

PARENT_DATA CHILD_DATA PARENT_VERSION CHILD_VERSION
---------------------------------------------------
20-10       40-01       -A11        -A11
20-10       40-02       -A11        -A11
20-10       40-03       -A11        -A11
80-10       40-100      -A11        -A11
80-10       40-101      -A11        -A11
80-10       40-102      -A11        -A11

Я пробовал с рекурсивным вызовом процедуры с курсором:

Это код:

create or replace procedure proc_repeat(in_parent         in  varchar2,
                                        IN_VERSION        IN  VARCHAR2,
                                        out_child         out varchar2,
                                        OUT_CHILD_VERSION OUT VARCHAR2)
as
  v_parent_Data varchar2(50);
  v_data varchar2(50);
  v_data_VERSION varchar2(50);
  v_data_RECUR varchar2(50);
  v_data_RECUR_VERSION varchar2(50);

  cursor cur_data is 
    select parent_data,child_data,child_version
      from sample_table
      where parent_data = in_parent AND
            parent_version = IN_VERSION;
begin
  open cur_data;
  loop
    fetch cur_data
      into v_parent_Data,v_data,v_data_VERSION;

    exit when cur_data%notfound;

    IF V_DATA LIKE '40-%' THEN
      OUT_CHILD:=V_DATA;
      OUT_CHILD_VERSION:=v_data_VERSION;
    ELSIF V_DATA LIKE '80-%' THEN
      v_data_RECUR:=V_DATA;
      v_data_RECUR_VERSION:=v_data_VERSION;

      proc_repeat(v_data_RECUR, v_data_RECUR_VERSION,
                  out_child, OUT_CHILD_VERSION);
    END IF;
  end loop;

  close cur_Data;
end;

ожидаемый результат:

CHILD_DATA
-----------
40-01
40-02
40-03
40-100
40-101
40-102

Но я получаю только '40 -102 '

DECLARE
  A VARCHAR2(50):='20-10';
  B VARCHAR2(50):='-A11';
  C VARCHAR2(50);
  D VARCHAR2(50);
BEGIN
  proc_repeat(A,B,C,D);
  DBMS_OUTPUT.PUT_LINE(C);
END;

1 Ответ

0 голосов
/ 23 сентября 2019

Давайте создадим инструментальную версию вашей процедуры, добавив несколько вызовов DBMS_OUTPUT.PUT_LINE.Это будет выглядеть так:

create or replace procedure proc_repeat_I1(in_parent         in  varchar2,
                                           IN_VERSION        IN  VARCHAR2,
                                           out_child         out varchar2,
                                           OUT_CHILD_VERSION OUT VARCHAR2)
as
  v_parent_Data varchar2(50);
  v_data varchar2(50);
  v_data_VERSION varchar2(50);
  v_data_RECUR varchar2(50);
  v_data_RECUR_VERSION varchar2(50);

  cursor cur_data is 
    select parent_data,child_data,child_version
      from sample_table
      where parent_data = in_parent AND
            parent_version = IN_VERSION;
begin
  DBMS_OUTPUT.PUT_LINE('PROC_REPEAT_I1 : START - IN_PARENT=' || IN_PARENT ||
                                    ' IN_VERSION=' || IN_VERSION ||
                                    ' OUT_CHILD=' || OUT_CHILD);

  open cur_data;
  loop
    fetch cur_data
      into v_parent_Data,v_data,v_data_VERSION;

    exit when cur_data%notfound;

    DBMS_OUTPUT.PUT_LINE('PROC_REPEAT_I1 : v_parent_Data=' || v_parent_Data ||
                                      ' v_data=' || v_data);

    IF V_DATA LIKE '40-%' THEN
      OUT_CHILD:=V_DATA;
      OUT_CHILD_VERSION:=v_data_VERSION;

      DBMS_OUTPUT.PUT_LINE('PROC_REPEAT_I1 : BLOCK 1 : OUT_CHILD = ' || OUT_CHILD);
    ELSIF V_DATA LIKE '80-%' THEN
      v_data_RECUR:=V_DATA;
      v_data_RECUR_VERSION:=v_data_VERSION;

      DBMS_OUTPUT.PUT_LINE('PROC_REPEAT_I1 : BLOCK 2 : OUT_CHILD = ' || OUT_CHILD ||
                           ' V_DATA_RECUR = ' || V_DATA_RECUR);

      proc_repeat_I(v_data_RECUR, v_data_RECUR_VERSION,
                    out_child, OUT_CHILD_VERSION);
    END IF;
  end loop;

  close cur_Data;

  DBMS_OUTPUT.PUT_LINE('PROC_REPEAT_I1 : EXIT');
end;

Когда мы запустим это, вы получите:

PROC_REPEAT_I1 : START - IN_PARENT=20-10 IN_VERSION=-A11 OUT_CHILD=
PROC_REPEAT_I1 : v_parent_Data=20-10 v_data=40-01
PROC_REPEAT_I1 : BLOCK 1 : OUT_CHILD = 40-01
PROC_REPEAT_I1 : v_parent_Data=20-10 v_data=40-02
PROC_REPEAT_I1 : BLOCK 1 : OUT_CHILD = 40-02
PROC_REPEAT_I1 : v_parent_Data=20-10 v_data=40-03
PROC_REPEAT_I1 : BLOCK 1 : OUT_CHILD = 40-03
PROC_REPEAT_I1 : v_parent_Data=20-10 v_data=80-10
PROC_REPEAT_I1 : BLOCK 2 : OUT_CHILD = 40-03 V_DATA_RECUR = 80-10
PROC_REPEAT_I1 : START - IN_PARENT=80-10 IN_VERSION=-A11 OUT_CHILD=
PROC_REPEAT_I1 : v_parent_Data=80-10 v_data=40-100
PROC_REPEAT_I1 : BLOCK 1 : OUT_CHILD = 40-100
PROC_REPEAT_I1 : v_parent_Data=80-10 v_data=40-101
PROC_REPEAT_I1 : BLOCK 1 : OUT_CHILD = 40-101
PROC_REPEAT_I1 : v_parent_Data=80-10 v_data=40-102
PROC_REPEAT_I1 : BLOCK 1 : OUT_CHILD = 40-102
PROC_REPEAT_I1 : EXIT
PROC_REPEAT_I1 : v_parent_Data=20-10 v_data=81-10
PROC_REPEAT_I1 : EXIT
40-102

Мы видим, что первый вызов процедуры из анонимного блока сделан, передавая'20-10' в качестве родителя и '-A11' в качестве родительской версии, а затем курсор перебирает '40-01 ', '40-02' и '40-03', пока не достигнет '80-10'.Как только он достигает '80-20', он переходит ко второму блоку и рекурсивно вызывает себя, затем перебирает '40-100', '40-101' и '40-102', как и ожидалось.

Так что же здесь не так?Ну ничего - кроме того, что нигде ваш код не заставляет значение из курсора либо накапливаться в OUT_CHILD, либо печататься.Итак, давайте изменим нашу инструментальную версию, чтобы накапливать значения, возвращаемые курсором, в OUT_CHILD:

create or replace procedure proc_repeat_I2(in_parent         in  varchar2,
                                           IN_VERSION        IN  VARCHAR2,
                                           out_child         out varchar2,
                                           OUT_CHILD_VERSION OUT VARCHAR2)
as
  v_parent_Data varchar2(50);
  v_data varchar2(50);
  v_data_VERSION varchar2(50);
  v_data_RECUR varchar2(50);
  v_data_RECUR_VERSION varchar2(50);

  cursor cur_data is 
    select parent_data,child_data,child_version
      from sample_table
      where parent_data = in_parent AND
            parent_version = IN_VERSION;
begin
  DBMS_OUTPUT.PUT_LINE('PROC_REPEAT : START - IN_PARENT=' || IN_PARENT ||
                                    ' IN_VERSION=' || IN_VERSION ||
                                    ' OUT_CHILD=' || OUT_CHILD);

  open cur_data;
  loop
    fetch cur_data
      into v_parent_Data,v_data,v_data_VERSION;

    exit when cur_data%notfound;

    DBMS_OUTPUT.PUT_LINE('PROC_REPEAT : v_parent_Data=' || v_parent_Data ||
                                      ' v_data=' || v_data);

    IF V_DATA LIKE '40-%' THEN
      OUT_CHILD := CASE
                     WHEN OUT_CHILD IS NULL THEN OUT_CHILD
                     ELSE OUT_CHILD || ', '
                   END || V_DATA;
      OUT_CHILD_VERSION := CASE
                             WHEN OUT_CHILD_VERSION IS NULL THEN OUT_CHILD_VERSION
                             ELSE OUT_CHILD_VERSION || ', '
                           END || V_DATA_VERSION;

      DBMS_OUTPUT.PUT_LINE('BLOCK 1 : OUT_CHILD = ' || OUT_CHILD);
    ELSIF V_DATA LIKE '80-%' THEN
      v_data_RECUR:=V_DATA;
      v_data_RECUR_VERSION:=v_data_VERSION;

      DBMS_OUTPUT.PUT_LINE('BLOCK 2 : OUT_CHILD = ' || OUT_CHILD ||
                           ' V_DATA_RECUR = ' || V_DATA_RECUR);

      proc_repeat_I2(v_data_RECUR, v_data_RECUR_VERSION,
                     out_child, OUT_CHILD_VERSION);
    END IF;
  end loop;

  close cur_Data;

  DBMS_OUTPUT.PUT_LINE('PROC_REPEAT : EXIT');
end;

Но это все же не дает правильного результата!Вот вывод:

PROC_REPEAT_I2 : START - IN_PARENT=20-10 IN_VERSION=-A11 OUT_CHILD=
PROC_REPEAT_I2 : v_parent_Data=20-10 v_data=40-01
PROC_REPEAT_I2 : BLOCK 1 : OUT_CHILD = 40-01
PROC_REPEAT_I2 : v_parent_Data=20-10 v_data=40-02
PROC_REPEAT_I2 : BLOCK 1 : OUT_CHILD = 40-01, 40-02
PROC_REPEAT_I2 : v_parent_Data=20-10 v_data=40-03
PROC_REPEAT_I2 : BLOCK 1 : OUT_CHILD = 40-01, 40-02, 40-03
PROC_REPEAT_I2 : v_parent_Data=20-10 v_data=80-10
PROC_REPEAT_I2 : BLOCK 2 : OUT_CHILD = 40-01, 40-02, 40-03 V_DATA_RECUR = 80-10
PROC_REPEAT_I2 : START - IN_PARENT=80-10 IN_VERSION=-A11 OUT_CHILD=
PROC_REPEAT_I2 : v_parent_Data=80-10 v_data=40-100
PROC_REPEAT_I2 : BLOCK 1 : OUT_CHILD = 40-100
PROC_REPEAT_I2 : v_parent_Data=80-10 v_data=40-101
PROC_REPEAT_I2 : BLOCK 1 : OUT_CHILD = 40-100, 40-101
PROC_REPEAT_I2 : v_parent_Data=80-10 v_data=40-102
PROC_REPEAT_I2 : BLOCK 1 : OUT_CHILD = 40-100, 40-101, 40-102
PROC_REPEAT_I2 : EXIT
PROC_REPEAT_I2 : v_parent_Data=20-10 v_data=81-10
PROC_REPEAT_I2 : EXIT
40-100, 40-101, 40-102

Здесь мы можем видеть, что родительские значения, по-видимому, накапливаются, пока мы не перейдем в блок 2 и не сделаем рекурсивный вызов PROC_REPEAT_I2, после чего ... ОНИ УХОДЯТ!AGGGHHHH!ЧТО ПРОИЗОШЛО?!?!?!?

Что ж, интересный факт о PL / SQL - когда вы вызываете подпрограмму с параметром OUT, для параметра OUT в начале процедуры устанавливается значение NULL.Если вы хотите, чтобы значение, которое уже есть в аргументе процедуры, было сохранено, вам нужно определить аргумент как IN OUT вместо OUT.Итак, мы получаем нашу третью версию этой процедуры:

create or replace procedure PROC_REPEAT_I3(in_parent         in  varchar2,
                                           IN_VERSION        IN  VARCHAR2,
                                           out_child         IN OUT varchar2,
                                           OUT_CHILD_VERSION IN OUT VARCHAR2)
as
  v_parent_Data varchar2(50);
  v_data varchar2(50);
  v_data_VERSION varchar2(50);
  v_data_RECUR varchar2(50);
  v_data_RECUR_VERSION varchar2(50);

  cursor cur_data is 
    select parent_data,child_data,child_version
      from sample_table
      where parent_data = in_parent AND
            parent_version = IN_VERSION;
begin
  DBMS_OUTPUT.PUT_LINE('PROC_REPEAT_I3 : START - IN_PARENT=' || IN_PARENT ||
                                    ' IN_VERSION=' || IN_VERSION ||
                                    ' OUT_CHILD=' || OUT_CHILD);

  open cur_data;
  loop
    fetch cur_data
      into v_parent_Data,v_data,v_data_VERSION;

    exit when cur_data%notfound;

    DBMS_OUTPUT.PUT_LINE('PROC_REPEAT_I3 : v_parent_Data=' || v_parent_Data ||
                                      ' v_data=' || v_data);

    IF V_DATA LIKE '40-%' THEN
      OUT_CHILD := CASE
                     WHEN OUT_CHILD IS NULL THEN OUT_CHILD
                     ELSE OUT_CHILD || ', '
                   END || V_DATA;
      OUT_CHILD_VERSION := CASE
                             WHEN OUT_CHILD_VERSION IS NULL THEN OUT_CHILD_VERSION
                             ELSE OUT_CHILD_VERSION || ', '
                           END || V_DATA_VERSION;

      DBMS_OUTPUT.PUT_LINE('PROC_REPEAT_I3 : BLOCK 1 : OUT_CHILD = ' || OUT_CHILD);
    ELSIF V_DATA LIKE '80-%' THEN
      v_data_RECUR:=V_DATA;
      v_data_RECUR_VERSION:=v_data_VERSION;

      DBMS_OUTPUT.PUT_LINE('PROC_REPEAT_I3 : BLOCK 2 : OUT_CHILD = ' || OUT_CHILD ||
                           ' V_DATA_RECUR = ' || V_DATA_RECUR);

      proc_repeat_I3(v_data_RECUR, v_data_RECUR_VERSION,
                  out_child, OUT_CHILD_VERSION);
    END IF;
  end loop;

  close cur_Data;

  DBMS_OUTPUT.PUT_LINE('PROC_REPEAT_I3 : EXIT');
end;

, и результат этой версии выглядит следующим образом:

PROC_REPEAT_I3 : START - IN_PARENT=20-10 IN_VERSION=-A11 OUT_CHILD=
PROC_REPEAT_I3 : v_parent_Data=20-10 v_data=40-01
PROC_REPEAT_I3 : BLOCK 1 : OUT_CHILD = 40-01
PROC_REPEAT_I3 : v_parent_Data=20-10 v_data=40-02
PROC_REPEAT_I3 : BLOCK 1 : OUT_CHILD = 40-01, 40-02
PROC_REPEAT_I3 : v_parent_Data=20-10 v_data=40-03
PROC_REPEAT_I3 : BLOCK 1 : OUT_CHILD = 40-01, 40-02, 40-03
PROC_REPEAT_I3 : v_parent_Data=20-10 v_data=80-10
PROC_REPEAT_I3 : BLOCK 2 : OUT_CHILD = 40-01, 40-02, 40-03 V_DATA_RECUR = 80-10
PROC_REPEAT_I3 : START - IN_PARENT=80-10 IN_VERSION=-A11 OUT_CHILD=40-01, 40-02, 40-03
PROC_REPEAT_I3 : v_parent_Data=80-10 v_data=40-100
PROC_REPEAT_I3 : BLOCK 1 : OUT_CHILD = 40-01, 40-02, 40-03, 40-100
PROC_REPEAT_I3 : v_parent_Data=80-10 v_data=40-101
PROC_REPEAT_I3 : BLOCK 1 : OUT_CHILD = 40-01, 40-02, 40-03, 40-100, 40-101
PROC_REPEAT_I3 : v_parent_Data=80-10 v_data=40-102
PROC_REPEAT_I3 : BLOCK 1 : OUT_CHILD = 40-01, 40-02, 40-03, 40-100, 40-101, 40-102
PROC_REPEAT_I3 : EXIT
PROC_REPEAT_I3 : v_parent_Data=20-10 v_data=81-10
PROC_REPEAT_I3 : EXIT
40-01, 40-02, 40-03, 40-100, 40-101, 40-102

Наконец, получаются правильные результаты.

Итак, помните - когда что-то не работает, попробуйте распечатать промежуточные результаты, чтобы вы могли видеть, что происходит.

dbfiddle здесь

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...