Oracle分层递归查询在循环中停止并继续使用其他包名称

Oracle hierarchical recursive query stop at cycle and continue with other package name

我现在正为以下问题而苦恼。

我想知道哪个包调用了哪个包。 table 不是分层的。 它是 table user_dependencies.

目前的代码:

CREATE OR REPLACE PACKAGE object_x is
 
  type ObjectRec is record(
    dName      varchar2(250),
    level       number
  );
 
  type ObjectTemp is table of ObjectRec;
 
  function Referenced(dname VARCHAR2, level NUMBER, maxl NUMBER) return ObjectTemp pipelined;
 
end;
/
 
CREATE OR REPLACE PACKAGE BODY object_x is
 
  function Referenced(dname VARCHAR2, level NUMBER, maxl NUMBER) return ObjectTemp pipelined is
    rData  ObjectRec;

  begin
 
    if level >= maxl then
      return;
    end if;
  
    if level = 1 then
      rData.dName := name;
      rData.Level := maxl;
      pipe row(rData);
    end if;
 
    for r in ( 
      select referenced_name
      from user_dependencies
      where name = upper(dname)
      and type = 'PACKAGE BODY'
      and referenced_type = 'PACKAGE'
      and referenced_name != UPPER(dname)
      and referenced_name != name 
    ) 

      loop
      rData.dName := LPAD(' ', 3, ' ') || r.Referenced_name;
      rData.level := level+1;
      pipe row(rData);
      rData.Name := r.Referenced_name;
 
 
     for r2 in (select * from table(Referenced(rData.Name, level + 1, maxl))) loop
        rData.Name := LPAD(' ', 3, ' ') || r2.dName;
        rData.Level := r2.Level;
        pipe row(rData);
        null;
      end loop;
 
    end loop;

结果:

     Level Dname
---------- --------------------------------------------------------------------------------
         1 PAC1
         2    PAC2
         2    PAC3
         2    PAC4
         2    PAC5
         3       PAC6
         3       PAC2
         3       PAC7
         3       PAC8
         4          PAC9
         4          PAC10
         5             PAC6
         5             PAC11
         5             PAC3
         5             PAC9
         5             PAC12
         5             PAC6
         5             PAC3
         5             PAC9
         5             PAC4
         5             PAC8
         3       PAC10
         4          PAC6
         4          PAC11
         4          PAC3
         4          PAC9
         4          PAC12
         4          PAC4

预期结果:

     Level Dname
---------- --------------------------------------------------------------------------------
         1 PAC1
         2    PAC2
         2    PAC3
         2    PAC4
         2    PAC5
         3       PAC6
         3       PAC2
         3       PAC7
         3       PAC8
         4          PAC9
         4          PAC10
         5             PAC6
         5             PAC11
         5             PAC3
         5             PAC9
         5             PAC12
         5             PAC6
         5             PAC3
         5             PAC9
         5             PAC4
         5             PAC8
         3       PAC10 LOOP!!!!
          -----BREAK------
         CONTINUE WITH OTHER PACKAGES……….. 

感谢任何建议。

最终,是的,可以做你想做的事。

实现此目的的一种方法是使用嵌套 table 来存储您通过当前递归函数调用链构建的依赖包链。您可以使用带有此嵌套 table 的 MEMBER OF 运算符来检查包是否已在此链中(因此您已找到循环)。如果没有,您可以递归调用 Referenced 函数,但为当前包向此 table 添加一个额外项目。

我对您的代码进行了这些更改,结果如下:

CREATE OR REPLACE TYPE varchar2_table AS TABLE OF VARCHAR2(250);
/

CREATE OR REPLACE PACKAGE object_x is
 
  type ObjectRec is record(
    dName      varchar2(250),
    dLevel     number
  );
 
  type ObjectTemp is table of ObjectRec;
 
  function Referenced(dname VARCHAR2, dlevel NUMBER, maxl NUMBER) return ObjectTemp pipelined;
  function Referenced(dname VARCHAR2, dlevel NUMBER, maxl NUMBER, pkgs_so_far IN varchar2_table) return ObjectTemp pipelined;

end;
/
 
CREATE OR REPLACE PACKAGE BODY object_x is

  function Referenced(dname VARCHAR2, dlevel NUMBER, maxl NUMBER) return ObjectTemp pipelined is
    rData  ObjectRec;
  begin
    for r2 in (select * from table(Referenced(dname, dlevel, maxl, varchar2_table(dname))))
    loop
      rData.dName := r2.dName;
      rData.dLevel := r2.dLevel;
      pipe row(rData);
    end loop;
  end;
 
  function Referenced(dname VARCHAR2, dlevel NUMBER, maxl NUMBER, pkgs_so_far IN varchar2_table) return ObjectTemp pipelined is
    rData  ObjectRec;
    new_pkgs_so_far varchar2_table;
    loop_detected boolean;
  begin
 
    if dlevel >= maxl then
      return;
    end if;
  
    if dlevel = 1 then
      rData.dName := dname;
      rData.dLevel := 1;
      pipe row(rData);
    end if;
 
    for r in ( 
      select referenced_name
      from user_dependencies
      where name = upper(dname)
      and type = 'PACKAGE BODY'
      and referenced_type = 'PACKAGE'
      and referenced_name != UPPER(dname)
      and referenced_name != name 
    ) 

    loop
      loop_detected := r.referenced_name MEMBER OF pkgs_so_far;
    
      rData.dName := LPAD(' ', 3, ' ') || r.Referenced_name;
      if loop_detected then
        rData.dName := rData.dName || ' --- LOOP';
      end if;
      rData.dLevel := dLevel + 1;
      pipe row(rData);
 
      if not loop_detected THEN
        new_pkgs_so_far := pkgs_so_far;
        new_pkgs_so_far.EXTEND(1);
        new_pkgs_so_far(new_pkgs_so_far.COUNT) := r.referenced_name;
        for r2 in (select * from table(Referenced(r.referenced_name, dLevel + 1, maxl, new_pkgs_so_far))) loop
          rData.dName := LPAD(' ', 3, ' ') || r2.dName;
          rData.dLevel := r2.dLevel;
          pipe row(rData);
        end loop;
      end if;
 
    end loop;
  end;
end;
/

我已经修改了您的流水线函数以添加 pkgs_so_far 参数,其中包含当前遇到的 table 个包。我添加了另一个与您的原始函数具有相同签名的流水线函数,这只是调用另一个具有仅包含给定包的 table 包的函数。

在修改后的函数中,我们现在使用 MEMBER OF 运算符来检查我们是否已经遇到当前包。如果有,我们可以添加一个额外的 --- LOOP 标记来表明这一点。如果我们之前没有遇到过这个包,我们将包名添加到 table 并继续递归检查它的引用。请注意,在 new_pkgs_so_far := pkgs_so_far; 行中分配 table 似乎将 new_pkgs_so_far 设置为 pkgs_so_far 中 table 的副本,因此我们可以变异 new_pkgs_so_far 不影响 pkgs_so_far.

但是,我还用您的代码解决了一些其他问题:

  • 包体似乎缺少最后两行 end;,一行结束程序,另一行结束包体本身。
  • 您的代码包含对您记录的 name 字段的一些引用,但它被命名为 dName。我整理了这些,以便使用 dName.
  • Oracle 似乎不喜欢使用名称 level,因此我将其更改为 dLevel。 (LEVEL 是一个 Oracle 关键字,它用于分层 SQL。)
  • 我不确定在变量 dlevel 为 1 的情况下将 dLevel 设置为 maxl 的行输出是否正确,所以我更改了这个改为 1
  • 当递归调用你的 References 函数时,我将第一个参数更改为 r.referenced_name,因为这样我们就不需要分配给 rData.dName.
  • 我在循环中删除了一个不必要的 null; 语句,该循环遍历对您的过程的递归调用。

我也注意到你写了两次 LPAD(' ', 3, ' '),但我不确定为什么:这是 long-winded 写 ' ' 的方式,其中有三个字符串中的空格。

其中一些错误可能是因为您在将代码发布到此处之前将代码匿名化了,但在发布之前没有检查您发布的代码是否编译。以后请确保您的代码可以编译,否则任何想帮助您的人都必须先修复您的编译错误。

我也没有你所有的包(我怀疑实际上它们没有命名为 PAC1PAC2 等等),所以我创建了三个包 PAC1PAC3PAC2PAC3都互相引用,PAC1,我得到的输出是这样的:

SQL> COLUMN dname FORMAT a30
SQL> select * from table(object_x.referenced('PAC2', 1, 10));

DNAME                              DLEVEL
------------------------------ ----------
PAC2                                    1
   PAC1                                 2
   PAC3                                 2
      PAC1                              3
      PAC2 --- LOOP                     3