Question

I'm trying to sort out some PL/SQL code, trying to test the performance. Being able to do this will allow us to make much fewer calls the the DB, but I'm not sure how to fill the returned type I've created. Here's a poorly designed table structure that demonstrates what I'm trying to accomplish.

create table emp_group (
  gid number,
  gname varchar2(10)
);

create table emp (
  empID number,
  gid number,
  empname varchar2(10)
);

create or replace type t_emp_obj as object (
  empID number,
  gid number,
  empname varchar2(10)
);

create or replace type t_emp is table of t_emp_obj;

create or replace type t_group_emp as object
(
  gid number,
  gname varchar2(10),
  g_emps t_emp
);

insert into emp( empID, gid, empName ) values ( 1, 10, 'Rob' );
insert into emp( empID, gid, empName ) values ( 2, 10, 'Ken' );
insert into emp( empID, gid, empName ) values ( 3, 10, 'Dave' );
insert into emp( empID, gid, empName ) values ( 4, 10, 'Ron' );
insert into emp( empID, gid, empName ) values ( 5, 11, 'Joe' );
insert into emp_group( gid, gname ) values (10, 'DDP');
insert into emp_group( gid, gname ) values (11, 'DDD');
commit;


create or replace function f_test1 return t_emp as
  ret t_emp;
begin
  select t_emp_obj(empID, gid, empname) bulk collect into ret from emp;

  return ret;
end;


create or replace function f_test2 return t_group_emp as
  ret t_group_emp;
begin
  select t_group_emp(????) bulk collect into ret
  from emp, emp_group
  where emp.gid = emp_group.gid;

  return ret;
end;

And here's a function to run through it.

set serveroutput on size 10000

declare
  x t_emp;
begin
  x := f_test1;

  for r in (select * from table(cast(x as t_emp))) loop
    dbms_output.put_line(r.empID || ', ' || r.gid || ', ' || r.empname );
  end loop;

end;

We have two tables, one a list of employees and one a list of employee groups. Ignore the fact that there should be a join table between emp_group and emp so that an employee can belong to more than one group... demo code. 8)

I would like to return all of the employees for a given group ID, along with the groups row, in a single returned type. The returned type is t_group_emp, which has a table of emp rows. What's the syntax for f_test2?

1) Is this possible?

2) How would I structure the select statement to fill the returned type? Is 'bulk collect' the correct method here? I found this page to be a good starting point.

3) How poorly will this perform? I can run my own numbers once I get the syntax sorted, but overall, is this a good approach to returning data from multiple tables with a 1-N relationship?

It will be easy to code the application to consume the returned type, so I'm not worried about that.

edit: we're using Oracle 10.3.something...

EDIT: this is close to what I want, but not quite.

create or replace
function f_test2 return t_group as
  ret t_group;
begin
  select t_group_emp( emp_group.gid, emp_group.gname, t_emp( t_emp_obj( emp.empID, emp.gid, emp.empName ))) bulk collect into ret
  from emp, emp_group 
  where emp.gid = emp_group.gid;

  return ret;
end;

and a function to run through that one...

set serveroutput on size 10000

declare
  x t_group;
begin
  x := f_test2;

  for r in (select * from table(cast(x as t_group))) loop
    dbms_output.put_line( 'group ' || r.gid || ', ' || r.gname  );
    for s in (select * from table(cast(r.g_emps as t_emp))) loop
      dbms_output.put_line( 'emp ' || s.empID || ', ' || s.empname  );
    end loop;  
  end loop;

end;

Gives the following output:

group 10, DDP
emp 1, Rob
group 10, DDP
emp 2, Ken
group 10, DDP
emp 3, Dave
group 10, DDP
emp 4, Ron
group 11, DDD
emp 5, Joe

Which is great, but I want the output to look like this, to show the 1-N relationship.

group 10, DDP
emp 1, Rob
emp 2, Ken
emp 3, Dave
emp 4, Ron
group 11, DDD
emp 5, Joe

What do I need to change in f_test2 function to achieve that?

Was it helpful?

Solution

First off: I am not currently able to access/use an oracle installation. I tried SQL Fiddle, but this kind of problem seems too complex for it (or I am too dumb, which is all too possible :) ). So I could not test this out.

Anyway, you should be able to achieve your goal with something like this:

  select t_group_emp(g.gid, 
                     g.gname, 
                     cast(multiset(select e.empID, 
                                          e.gid, 
                                          e.empname 
                                     from emp e
                                    where e.gid = g.gid
                                  ) as t_emp
                         )
                    )
    from emp_group g

The aliases might not be neccessary, it is something I do automatically to reduce typing.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top