O'Reilly Databases

oreilly.comSafari Books Online.Conferences.

We've expanded our coverage and improved our search! Search for all things Database across O'Reilly!

Search Search Tips

advertisement
AddThis Social Bookmark Button

Listen Print Subscribe to Databases Subscribe to Newsletters

Multi-Level Collections in Oracle 9i
Pages: 1, 2, 3, 4

Reporting on the Nested Table

We'll also need some code to extract the information from the nested table and show to a user of this application. The following cursor for loop retrieves the name of the person and his training log and then iterates through that log, displaying its contents. See Example 4 for the complete code.



begin
  for v_row in (select first_name, training_log
                  from nested_training_logs)
  loop
     show (v_row.first_name);

     for week in 
        v_row.training_log.first .. 
        v_row.training_log.last
     loop
        show (week);

        for run in
          v_row.training_log (week).first .. 
          v_row.training_log (week).last
        loop
          show (run);
          show (v_row.training_log (week) (run).distance);
          show (v_row.training_log (week) (run).pace);
        end loop;
     end loop;
  end loop;
end;

Notice that in the preceding code we see that appending each successive subscript to the variable representing the multi-level collection instance drills down into each successive layer in its structure.

If appropriate, this could be rewritten using bulk collect into local multi-level collection (with one extra level), thus:

declare
  type first_name_tab_t is table of 
    nested_training_logs.first_name%type
    index by binary_integer;
  v_first_name_tab first_name_tab_t;

  type training_logs_tab_t is table of training_log_t
    index by binary_integer;
  v_training_logs_tab training_logs_tab_t;
begin
  select first_name, training_log
    bulk collect into 
      v_first_name_tab, v_training_logs_tab
    from nested_training_logs;  

  for j in 
      v_first_name_tab.first..
      v_first_name_tab.last
  loop
    Show ( v_first_name_tab(j) );
    for week in v_training_logs_tab(j).first..
                v_training_logs_tab(j).last
    loop
      Show ( week );
      for run in v_training_logs_tab(j)(week).first..
                 v_training_logs_tab(j)(week).last
      loop
        Show ( run );
        Show ( v_training_logs_tab(j)(week)(run).distance );
        Show ( v_training_logs_tab(j)(week)(run).pace     );
      end loop;
    end loop;
  end loop;
end;

As you can see, we simply add another row specifier since we have an additional level of nesting:

v_training_logs_tab(j)(week)(run).distance

Example 4. Report on the contents of the nested table.

begin
  for v_row in
    ( select first_name, training_log from nested_training_logs )
  loop
    dbms_output.put_line ( v_row.first_name );
    for week in v_row.training_log.first..
                v_row.training_log.last
    loop
      dbms_output.put_line ( '. week #' || to_char(week) );
      for run in v_row.training_log(week).first..
                 v_row.training_log(week).last
      loop
        dbms_output.put_line
          (
            '.   run #' || to_char(run) || ': '
||          lpad ( v_row.training_log(week)(run).distance, 3, ' ' )
||          ' /'
||          lpad ( v_row.training_log(week)(run).pace,     3, ' ' )
          );
      end loop;
    end loop;
  end loop;
end;

Deriving a table function from the reporting logic to output a relational "view" At the heart of the innermost loop in the preceding section, we have the required information to populate a record corresponding to one row of the relational representation. (Writing a program to generate a report is a convenient way to test the logic before converting the code to a table function.) The conversion is relatively routine:

  1. Surround the block with a create function statement and declare a ref cursor input parameter.

  2. Define types for a record, and table of such records, according to the requirement and add a return declaration for the record type.

  3. Add the pipelined keyword.

  4. Declare local variables v_in_row and v_out_row as records of the appropriate types.

  5. Reformulate the cursor loop (if it's not already coded this way) to use fetch p_in_cursor into v_in_row with the corresponding exit condition (don't open it—this is done by the system when the table function is invoked).

  6. Replace the Show invocations with assignments for the elements of the target record.

  7. Deliver the record as the actual parameter to pipe row().

  8. Add close p_in_cursor and return as the last executable statements.

With these changes in place (see Example 5), we can now conveniently perform ad hoc queries, such as that shown here:

select first_name, avg ( distance ) d, avg ( pace ) p
  from
    (
      select first_name, distance, pace
        from table
          (
            reln_training_logs_fn 
             (
               cursor
                 (
                   select first_name, training_log 
                     from nested_training_logs
                 )
             )
          )
    )
  group by first_name;

Example 5. Table function to "view" the contents of the nested table as a relational table.

create or replace function reln_training_logs_fn
  ( p_nested_training_logs in sys_refcursor )
  return my_types.reln_training_logs_tab_t
  /* 
    The algorithm handles each row in isolation and thus
    is amenable to the simplest form of parallelism
  */
  parallel_enable ( 
    partition p_nested_training_logs by any )  pipelined
is
  v_in_row  my_types.nested_training_log_row_t;
  v_out_row my_types.reln_training_log_row_t;
begin
  loop
    fetch p_nested_training_logs into v_in_row;
    exit when p_nested_training_logs%notfound;

    for week in v_in_row.training_log.first..
                v_in_row.training_log.last
    loop
      for run in v_in_row.training_log(week).first..
                 v_in_row.training_log(week).last
      loop
        v_out_row.first_name := 
           v_in_row.first_name;
        v_out_row.week       := 
           week;
        v_out_row.run        := 
           run;
        v_out_row.distance   := 
           v_in_row.training_log(week)(run).distance;
        v_out_row.pace       := 
           v_in_row.training_log(week)(run).pace;
        pipe row ( v_out_row );
      end loop;
    end loop;
  end loop;
  close p_nested_training_logs;
  return;
end reln_training_logs_fn;

Pages: 1, 2, 3, 4

Next Pagearrow




Tagged Articles

Be the first to post this article to del.icio.us

Sponsored Resources

  • Inside Lightroom

Related to this Article

Understanding Oracle Clinical Understanding Oracle Clinical
by Joan M. Johnson
June 2009
$9.99 USD

New Features in Oracle 9i New Features in Oracle 9i
by Howard J. Rogers
June 2009
$5.95 USD

Advertisement
O'Reilly Media

©2009, O'Reilly Media, Inc.
(707) 827-7000 / (800) 998-9938
All trademarks and registered trademarks appearing on oreilly.com are the property of their respective owners.
About O'Reilly
Academic Solutions
Authors
Contacts
Customer Service
Jobs
Newsletters
O'Reilly Labs
Press Room
Privacy Policy
RSS Feeds
Terms of Service
User Groups
Writing for O'Reilly
Content Archive
Business Technology
Computer Technology
Google
Microsoft
Mobile
Network
Operating System
Digital Photography
Programming
Software
Web
Web Design
More O'Reilly Sites
O'Reilly Radar
Ignite
Tools of Change for Publishing
Digital Media
Inside iPhone
makezine.com
craftzine.com
hackszine.com
perl.com
xml.com

Partner Sites
InsideRIA
java.net
O'Reilly Insights on Forbes.com