【问题标题】:Manipulating collections操作集合
【发布时间】:2014-05-23 04:02:48
【问题描述】:

我尝试对不同元素(所有者、表格)的列表进行排序。 只需一个就很容易(而且非常快!),例如:

declare
TYPE tbl_list IS TABLE OF VARCHAR2(64);
l_tables tbl_list;
i number;
begin
  l_tables:=tbl_list();
  for i in 1..100000
    loop
      l_tables:= l_tables MULTISET UNION DISTINCT tbl_list('myTable');
    end loop;
    for i in l_tables.first..  l_tables.last
      loop
        dbms_output.put_line(l_tables(i));
      end loop;  
end;
/

我尝试对列表进行同样的操作,但失败了:

create or replace TYPE tbl_list2 IS OBJECT (l_owner  VARCHAR2(64),l_name  VARCHAR2(64));




declare
l_object tbl_list2;
i number;
begin
  l_object:=tbl_list2('','');

  for i in 1..100000
    loop
      l_object:= l_object MULTISET UNION DISTINCT tbl_list2('myOwner','MyTable');
    end loop;
    for i in l_object.first..  l_object.last
      loop
        dbms_output.put_line(l_object(i));
      end loop;  

end;
/

但我发现以下内容: PLS-00306:调用“MULTISET_UNION_DISTINCT”时参数的数量或类型错误

我们的目标是列出所有不同的(所有者、表),当然我不在乎您是否发现任何其他想法。

一个解决方案当然是两个单词的一个连接,但我想找到更优雅的!

编辑 @ThinkJet:

我喜欢你的解决方案。它比我的肮脏解决方案更优雅。 但是,您的解决方案比我的慢 70 倍! 那么我们怎样才能收敛到一个优雅的蚂蚁速度解决方案呢?

这是我的脏话:

declare
TYPE tbl_list IS TABLE OF VARCHAR2(64);
l_tables tbl_list;
i number;
begin
  l_tables:=tbl_list();
  for i in 1..100000
    loop
      l_tables:= l_tables MULTISET UNION DISTINCT tbl_list('myOwner'||','||'myTable');
    end loop;
    for i in l_tables.first..  l_tables.last
      loop
        dbms_output.put_line('OWNER='||REGEXP_SUBSTR(l_tables(i),'[^,]+', 1, 1));
        dbms_output.put_line('TABLE='||REGEXP_SUBSTR(l_tables(i),'[^,]+', 1, 1));
      end loop;  
end;

/

【问题讨论】:

  • 好的。只是为了确定......有问题的测试用例是否正确反映了您的需求?是像示例中这样的许多小查询还是相对较少的大数据集交集?
  • 是的,这个测试用例大致反映了我的需求
  • 请检查更新的答案...

标签: sql oracle collections plsql


【解决方案1】:

至少您在第二种情况下丢失了表定义。本声明:

create or replace TYPE tbl_list2 IS OBJECT (l_owner  VARCHAR2(64),l_name  VARCHAR2(64));

仅声明对象(或记录)类型,而不是表。

所以你需要分两步完成:

create or replace TYPE tbl_list_rec IS OBJECT (l_owner  VARCHAR2(64),l_name  VARCHAR2(64));
/

create or replace TYPE tbl_list2 as table of tbl_list_rec;
/

之后,您需要在脚本中进行一些语法更正:

declare
  l_object tbl_list2;
  i        number;
begin
  -- for list initialization it must be filled with constructed objects
  l_object := tbl_list2( tbl_list_rec('','') );

  for i in 1..100000 loop          

    -- 1. select values to variable
    -- 2. Fix constructor for list 
    select 
      l_object MULTISET UNION DISTINCT tbl_list2(tbl_list_rec('myOwner','MyTable'))
    into 
      l_object 
    from 
      dual;

    end loop;

    for i in l_object.first ..  l_object.last loop
      -- output separate fields, there are now default conversion from 
      -- user-defined objects to varchar2.
      dbms_output.put_line(l_object(i).l_owner || ',' || l_object(i).l_name);
    end loop;  

end;
/

更新

由于上下文切换次数较多,上述解决方案相对较慢。但是如果没有一些额外的工作,就不能直接在 PL/SQL 中进行复杂对象类型实例的比较。
为了让Oracle知道对象实例是相同还是不同,我们需要定义mapping or ordering method for object type。这两种方法都不允许,所以需要选择合适的一种。 MAP 方法执行得更快,在我们的例子中不需要排序,所以去吧:

create or replace TYPE tbl_list_rec2 AS OBJECT (
  l_owner  VARCHAR2(64),
  l_name  VARCHAR2(64),
  map member function get_key return varchar2
);
/

实施:

create or replace TYPE BODY tbl_list_rec2 AS 

  map member function get_key return varchar2
  is
  begin
    return l_owner||chr(1)||l_name;
  end;

end;
/

之后,可以在 PL/SQL 代码中测试对象的相等性,例如问题的第一个示例中的简单 varchar2

declare
  l_object tbl_list2a;
  i        number; 
begin
  l_object := tbl_list2a( tbl_list_rec2('','') );

  for i in 1..100000 loop          
    l_object := l_object MULTISET UNION DISTINCT tbl_list2a(tbl_list_rec2('myOwner','MyTable'));
  end loop;

  for i in l_object.first..  l_object.last loop
    dbms_output.put_line(l_object(i).l_owner || ',' || l_object(i).l_name);
  end loop;  

end;
/

【讨论】:

  • SQL> 创建或替换 TYPE tbl_list2 作为 tbl_list_rec 的表; 2 / 创建或替换 TYPE tbl_list2 作为 tbl_list_rec 的表; ORA-06545:PL/SQL:编译错误 - 编译中止 ORA-06550:第 0 行,第 0 列:PLS-00565:TBL_LIST2 必须作为潜在的 REF 目标(对象类型)完成
  • 您好,感谢您的快速回答。 tbl_list_rec 的创建没问题,但是使用 tbl_list_rec 我得到了 PLS-00565。
  • 请检查 this answer about PLS-00565 on Oracle forums 是否是您的情况... 很快:如果您从 SQL*Plus 执行每个类型定义,请将 / 放在每个类型定义之后。
  • 好的,可以了。我更新了我的问题以提高知名度
  • 漂亮的解决方案!我喜欢它!脏的更快,但你的更清晰!谢谢
猜你喜欢
  • 2017-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-17
  • 2019-12-25
  • 1970-01-01
  • 1970-01-01
  • 2020-08-08
相关资源
最近更新 更多