Wednesday, April 10, 2013

Passing Multiple Values in a Parameter

The problem I had before me was to pass multiple account values in a parameter so I could retrieved the balance sum from GL Balances.  I looked on the web for a solution but I really did not find anything that would work.

I found that you can use a Table function to incorporate a PL/SQL collection in a query.  I needed to take a multiple value parameter and some how get it into a PL/SQL collection.  This multiple value parameter will be a list of values delimited by a coma.  For the PL/SQL collection, I decided to use a nested table.

In summary this is what I need to do:

  1. Read a multiple value parameter.
  2. Undelimit the values.
  3. Put each value in its own row of a nested table.
  4. Use the Table function to query against the nested table.
For this example I will use the Scott schema.  This example will take a parameter with multiple EMPNO numbers and return the sum of the salaries.  To test then it would be like this:
select  TEST_PACKAGE.GET_SALARY_TOTAL('7654,7566,7876') total_salary from dual

I am going use a little bit of the object oriented programming of PL/SQL. 
Run the script below to create a Type object varchar2.  Then the second part of the script will create a nested table Type for the multiple values:

create or replace
TYPE t_tf_row AS OBJECT (
  empno  VARCHAR2(5));

create or replace
TYPE t_tf_tab IS TABLE OF t_tf_row;

Here is the PL/SQL package:

create or replace
PACKAGE TEST_PACKAGE AS 

  FUNCTION GET_SALARY_TOTAL(P_EMPLOYEE_LIST IN VARCHAR2) RETURN NUMBER; 
  FUNCTION GET_ROWS(p_employee_list varchar2) RETURN t_tf_tab;

END TEST_PACKAGE;
/
create or replace
PACKAGE BODY TEST_PACKAGE AS

  FUNCTION GET_SALARY_TOTAL(P_EMPLOYEE_LIST IN VARCHAR2) RETURN NUMBER AS
  
  SUM_SAL NUMBER;
  
 
  BEGIN
       
  select sum(e.sal)
   into sum_sal
   from emp e
        where e.empno in (select empno from table( get_rows(p_employee_list) ));
    
    RETURN SUM_SAL;
  END GET_SALARY_TOTAL;
  
  FUNCTION GET_ROWS(p_employee_list in varchar2) RETURN t_tf_tab AS
  EMPS t_tf_tab := t_tf_tab();
  t_len BINARY_INTEGER;
  l_start integer;
  l_end integer;
  
  BEGIN
  
  IF p_employee_list is null then
     t_len := 0;
  else
     t_len := REGEXP_COUNT(p_employee_list,',',1) + 1;
  end if;
 
l_start := 1; 
for i in 1..t_len
loop
    
    EMPS.EXTEND;
    if instr(p_employee_list,',',1,i) = 0 then
        EMPS(EMPS.LAST) := t_tf_row(substr(p_employee_list,l_start));
    else
        l_end := instr(p_employee_list,',',1,i);
        EMPS(EMPS.LAST) := t_tf_row(substr(p_employee_list,l_start,l_end - l_start));
        l_start := instr(p_employee_list,',',1,i) + 1;
    end if;

end loop;

  
  return(EMPS);
  
  END GET_ROWS;

END TEST_PACKAGE;
/

The function GET_ROWS takes in the multiple value parameter and returns a nested table that is populated. This is not to say it is the best way to accomplish this but it is the solution I came up with.