Reputation: 744
I try to sort a list of distinct elements (owner, table). It's easy (and very quick to!) with just one, for example:
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;
/
I try to so the same with a list but it's failed:
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;
/
But I catch the following: PLS-00306: wrong number or types of arguments in call to 'MULTISET_UNION_DISTINCT'
The goal is to have a list of all distinct (owner, tables), I don't care if you find any others idea of course.
A solution is of course a concatenation in one word of the two, but I would like to find more elegant!
EDIT @ThinkJet:
I love your solution. It's more elegant than my dirty solution. But, Your solution is bout 70 time slower than mine! So How could we converge to have a elegant ant speed solution?
Here my dirty one:
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;
/
Upvotes: 2
Views: 829
Reputation: 6745
At least you lost a table definition in second case. This statement:
create or replace TYPE tbl_list2 IS OBJECT (l_owner VARCHAR2(64),l_name VARCHAR2(64));
declares only object (or record) type, not a table.
So you need to do it in 2 steps:
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;
/
After that you need some syntax corrections in script:
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;
/
Solution above relatively slow because of big number of context switches. But comparison of complex object type instances can't be done directly in PL/SQL without some additional work.
To allow Oracle to know if object instances are same or different, we need to define mapping or ordering method for object type. Both types of methods not allowed, so there are need to choose proper one. MAP methods performs faster and there are no need for ordering in our case, so go for it:
create or replace TYPE tbl_list_rec2 AS OBJECT (
l_owner VARCHAR2(64),
l_name VARCHAR2(64),
map member function get_key return varchar2
);
/
Implementation:
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;
/
After that it's possible to test objects for equality in PL/SQL code like simple varchar2
in first example from question:
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;
/
Upvotes: 2