【问题标题】:Not getting the correct output through procedure execution通过过程执行没有得到正确的输出
【发布时间】:2019-09-21 23:41:59
【问题描述】:

我正在为下表(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;

【问题讨论】:

  • 你打印任何东西的唯一时间是在匿名块中,它会第一次调用你的过程,所以当然只输出一行。也许您还需要从程序内部打印一些东西。
  • 那么获得预期输出的可能方法是什么?
  • 在程序中调用dbms_output。很难确切地说出从哪里读到它。但是,输出是为了什么?您的问题是一般如何添加调试消息?
  • 您输入20-10 作为in_parent 的参数,但想用80-10 获得结果

标签: oracle plsql


【解决方案1】:

让我们通过添加一些 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,此时......他们消失了!啊啊啊!发生了什么?!?!?!?

嗯,关于 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 here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-24
    • 1970-01-01
    相关资源
    最近更新 更多