在 PL/SQL 中创建动态对象

Posted

技术标签:

【中文标题】在 PL/SQL 中创建动态对象【英文标题】:Create Dynamic Objects in PL/SQL 【发布时间】:2015-10-15 10:41:27 【问题描述】:

我对 PL/SQL 还很陌生,目前我需要一个特定的功能,我怀疑这在 PL/SQL 中是不可行的。我一定会使用 PL/SQL,因此不幸的是,关于其他编程(“查询”)语言的提示对我来说毫无用处。 因此,我想问一下,是否可以在程序流期间在 PL/SQL 中创建用户定义类型的实例,而不仅仅是在 DECLARE 块中,如果有可能,我想知道如何去做吧。

场景:

我想创建一个列表列表,例如...

TYPE SIMPLE_LIST IS TABLE OF VARCHAR2(30) INDEX BY VARCHAR(30);
TYPE LIST_OF_LISTS IS TABLE OF SIMPLE_LIST INDEX BY VARCHAR2(30);

类型的创建没有任何问题。

在我的程序中,我有一个函数,需要声明这样一个LIST_OF_LISTS并动态填充。

因此,简化的代码示例应如下所示...

FUNCTION foo(...) RETURN ...
AS
  ll LIST_OF_LISTS;
  sl SIMPLE_LIST;
  ...
BEGIN
  LOOP  -- iterate over something
  ...
  sl := new SIMPLE_LIST; -- this surely doesn't work
  sl('key1') := ...;
  sl('key2') := ...;
  sl('key3') := ...;
  ...
  ll('iter_key') := sl;
  END LOOP;
  RETURN ll;
END foo;

我想要/需要使用这样的列表列表,因为我无法在运行前确定每个列表(也不是列表列表)的长度。

正如人们已经知道的那样,我正在寻找一种类似于 OO 的功能来创建一个类型的实例,该类型具有类似于“新”运算符的东西,就在程序流程的中间,以填充列表动态列出。带有“新”运算符的行只是对我想要完成的任务的提示,因为我知道这不是包含所描述任务的方式。

谁能告诉我如何使用 PL/SQL 实现所描述的场景?

编辑

可能会感兴趣,这里有一些关于我正在尝试完成的实际任务的背景信息。简而言之,函数 'foo' 将从 xml 文档中提取一些项目并返回打包在数据结构中的结果以供以后处理,这就是我最终采用这种列表列表的方法的原因。 函数 foo 接收一个 xml 文档 (XMLTYPE),以及在文档解析期间要搜索的项目列表。在解析文档时,使用 DBMS_XMLDOM 包,列表中填充了与要搜索的元素之一匹配的每个 XML 标记的键和值。由于 XML 标记在整个文档中可能不是唯一的,而是多次出现,因此我想出了使用定义的 SIMPLE_LIST 来存储每次出现的 XML 标记/元素(键)的值到被搜索。因此,LIST_OF_LISTS 的“键”/“索引”最终应包含 XML 标记/元素的名称,而 SIMPLE_LIST 应包含相应 XML 标记/元素的任何出现的所有值,并打包在一个列表中。 要返回的列表中的条目数量将相当少(绝对不超过 100 个条目),因此我认为在这种情况下使用实际表或嵌套表可能会过大。

提前致谢。

克里斯

编辑²

我测试了 Boneist 和 Łukasiewicz 先生的答案,我可以确认它们在应用于我的场景时都有效。我接受了后者,因为它是最简洁的答案。

再次感谢您解决了我的问题。

干杯, 克里斯

【问题讨论】:

出于好奇,你打算用这个做什么?大概你要对存储在集合中的数据做点什么? 这是什么LIST_OF_LISTS?使用它的目的是什么?你可以先说明你的要求。好像是XY问题。你被问题 X 困住了,但最终解释了问题 Y,这是你对实际问题的解释。 如果您正在寻找多维数组的示例,this post 可能会有所帮助 我很抱歉对要完成的实际任务保持沉默。我已经编辑了我的问题,以便更好地了解我要解决的问题。 【参考方案1】:

也许这会有所帮助。

    declare 
      TYPE SIMPLE_LIST IS TABLE OF VARCHAR2(30) INDEX BY VARCHAR(30);
      TYPE LIST_OF_LISTS IS TABLE OF SIMPLE_LIST INDEX BY VARCHAR2(30);
        ll LIST_OF_LISTS;
        key_ll VARCHAR2(30);
        key_sl  VARCHAR2(30); 
    BEGIN
    --dynamic 
     for i in 1 .. 10 loop
        for j in 1..10 loop
          ll('i='||i)('j='||j) := 'value='||j;
        end loop;
     end loop;
-- static
     ll('A')('1'):='A1';
     ll('A')('2'):='A2';
     ll('A')('3'):='A3';
     ll('A')('4'):='A4';
     ll('A')('5'):='A5';    
     ll('B')('1'):='B1';
     ll('B')('2'):='B2';
     ll('B')('3'):='B3';
     ll('B')('4'):='B4';
     ll('B')('5'):='B5'; 



    -- and how to iterate it.
      key_ll := ll.first;
      while (key_ll is not null)
         loop
              key_sl := ll(key_ll).first;
              dbms_output.put_line(key_ll);
              while (key_sl is not null)
                 loop
                    dbms_output.put_line('   key sl: '||key_sl||' value sl: '||ll(key_ll)(key_sl));
                     key_sl :=  ll(key_ll).next(key_sl);
              end loop;

             key_ll := ll.next(key_ll);
      end loop;
    END foo;

【讨论】:

感谢您的回复,但恐怕它与我的场景不完全匹配。在您的示例中,您在迭代之前填充了 LIST_OF_LISTS 数据结构。但是,当我从一个全空列表开始并且我必须在迭代它时动态填充它时,这也会起作用吗?【参考方案2】:

我很难想出一个合理的理由来解释为什么你可能想要做一个嵌套关联数组这样的事情,但是在不知道你要解决什么问题的情况下,很难提出更好的方法正在做(尽管很可能存在一个!)。

话虽如此,我认为您正在苦苦挣扎的是一种将嵌套关联数组(即您的示例中的 simple_list )重置为空的方法,对于通过 list_of_lists 外部数组的每次迭代。有两种方法 - 一种是在处理 sl 数组的内容后使用 sl.delete(),另一种是将匿名 PL/SQL 块添加到处理中。

这是后者的一个例子:

declare
  type simple_list is table of varchar2(30) index by varchar(30);
  type list_of_lists is table of simple_list index by varchar2(30);

  ll_main list_of_lists;
  sub_sl_main simple_list;

  ll_idx varchar2(30);
  sl_idx varchar2(30);

  function foo
  return list_of_lists
  is
    ll list_of_lists;
  begin
    for j in 1..5
    loop

      declare
        sl simple_list;
      begin
        for i in 1..10
        loop
          if mod(i, j) = 0 then
            sl('key'||i) := 'value '||j||'_'||i;
          end if;
        end loop;

        ll('iter_key'||j) := sl;
      end;


    end loop;

    return ll;
  end;
begin
  ll_main := foo;

  ll_idx := ll_main.first;

  while (ll_idx is not null)
  loop    
    sub_sl_main := ll_main(ll_idx);

    sl_idx := sub_sl_main.first;

    while (sl_idx is not null)
    loop
      dbms_output.put_line('ll_idx = '||ll_idx||' and sl_idx = '||sl_idx||' and sub-list value = '||sub_sl_main(sl_idx));
      sl_idx := sub_sl_main.next(sl_idx);
    end loop;

    ll_idx := ll_main.next(ll_idx);
  end loop;
end;
/

ll_idx = iter_key1 and sl_idx = key1 and sub-list value = value 1_1
ll_idx = iter_key1 and sl_idx = key10 and sub-list value = value 1_10
ll_idx = iter_key1 and sl_idx = key2 and sub-list value = value 1_2
ll_idx = iter_key1 and sl_idx = key3 and sub-list value = value 1_3
ll_idx = iter_key1 and sl_idx = key4 and sub-list value = value 1_4
ll_idx = iter_key1 and sl_idx = key5 and sub-list value = value 1_5
ll_idx = iter_key1 and sl_idx = key6 and sub-list value = value 1_6
ll_idx = iter_key1 and sl_idx = key7 and sub-list value = value 1_7
ll_idx = iter_key1 and sl_idx = key8 and sub-list value = value 1_8
ll_idx = iter_key1 and sl_idx = key9 and sub-list value = value 1_9
ll_idx = iter_key2 and sl_idx = key10 and sub-list value = value 2_10
ll_idx = iter_key2 and sl_idx = key2 and sub-list value = value 2_2
ll_idx = iter_key2 and sl_idx = key4 and sub-list value = value 2_4
ll_idx = iter_key2 and sl_idx = key6 and sub-list value = value 2_6
ll_idx = iter_key2 and sl_idx = key8 and sub-list value = value 2_8
ll_idx = iter_key3 and sl_idx = key3 and sub-list value = value 3_3
ll_idx = iter_key3 and sl_idx = key6 and sub-list value = value 3_6
ll_idx = iter_key3 and sl_idx = key9 and sub-list value = value 3_9
ll_idx = iter_key4 and sl_idx = key4 and sub-list value = value 4_4
ll_idx = iter_key4 and sl_idx = key8 and sub-list value = value 4_8
ll_idx = iter_key5 and sl_idx = key10 and sub-list value = value 5_10
ll_idx = iter_key5 and sl_idx = key5 and sub-list value = value 5_5

不过,我几乎可以肯定,有更好的方法来做你想做的事!

【讨论】:

非常感谢您的想法。我不知道在 PL/SQL 中使用嵌套函数的可能性。我会尽快试用。

以上是关于在 PL/SQL 中创建动态对象的主要内容,如果未能解决你的问题,请参考以下文章

使用执行立即参数和可选参数在 PL/SQL 中创建对象实例

Oracle ApeX (PL/SQL) - 从 JavaScript 变量中创建绑定变量

PL/SQL 私有对象方法

在 PL/SQL 中创建匿名块

在 pl/sql 中创建过程时出现错误

为啥我们要编写 create/replace 来在 PL/SQL 中创建过程