简体   繁体   中英

Create Dynamic Objects in PL/SQL

I'm pretty new to PL/SQL and currently I have the need for a specific functionality, which I suspect to not being doable with PL/SQL. I am bound to using PL/SQL, so hints on other programming ('query') languages will unfortunately be of no use to me. Therefore I'd like to ask, whether it is possible to create instances of user-defined types in PL/SQL during program flow and not only within the DECLARE block, and in case it is possible, I'd like to know how to do it.

Scenario:

I want to create a list of lists like...

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);

The creation of the type works without any problems.

In my program, I have a function, which has the need to declare such a LIST_OF_LISTS and fill it dynamically.

Therefore the simplified code sample shall look like...

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;

I want/need to use such a list of lists, because I cannot determine the length of each list (and also not of the list of lists) before runtime.

As one can already tell, I'm looking for an OO-like functionality to create an instance of a type with something like a 'new'-operator, right in the middle of the program flow, to fill the list of lists dynamically. The line with the 'new'-operator is just a hint on what I want to accomplish, as I am aware that this is not the way to encompass the described task.

Can anyone give me a hint on how I might be able to implement the described scenario using PL/SQL?

EDIT

As it might be of interest, here's a little more background information on the actual task I am trying to achieve. In a nutshell, the function 'foo' shall extract a few items from an xml document and return the result packed in a data structure for later processing, which is why I ended up with the approach of using such a list of lists. The function foo receives an xml document (XMLTYPE), as well as a list of items to be searched for during the parsing of the document. While the document is being parsed, using the DBMS_XMLDOM-package, the list is filled with key and value of each XML tag that matches one of the elements to be searched for. Due to the fact, that the XML tags may not be unique across the whole document but occur multiple times, I came up with the idea to use the defined SIMPLE_LIST to store the values of every single occurrence of XML tags/elements (keys) to be searched for. So the 'key'/'index' of LIST_OF_LISTS shall eventually contain the name of the XML tag/element, while the SIMPLE_LIST shall contain all values of any occurrence of the corresponding XML tag/element, packed together in one list. The amount of entries within the list to be returned will be rather small (definitely not more than 100 entries), therefore I assume that the use of actual tables or nested tables might be overkill in this case.

Thanks in advance.

Chris

EDIT²

I tested the answers of both Boneist and Mr. Łukasiewicz, and I can confirm that they both worked when applied to my scenario. I accepted the latter, due to the fact that it is the most compact answer.

Thanks again for solving my problem.

Cheers, Chris

Maybe this helps.

    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;

I'm struggling to think of a valid reason as to why you might want to do such a thing as a nested associative array, but without knowing what problem you're trying to solve, it's difficult to suggest a better way of doing (although it's highly likely that one exists!).

That being said, what I think you're struggling with is a way to reset the nested associative array (that's the simple_list one in your example) to be empty, for each iteration through the list_of_lists outer array. There are two ways - one is to use sl.delete() once you're doing with the contents of the sl array, and the other is to add an anonymous PL/SQL block into the processing.

Here's an example of the latter:

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

I'm almost certain there's a better way of doing what you're after, though!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM