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.

Friday, March 29, 2013

Company List in GL

I needed to have a list of companies that I needed for a reporting LOV.  Someone prior to me working on a project had a static table with a list of companies.  I'm sure you can that the approach is a bad idea.  One of my pet peeves is when someone uses DISTINCT in the selection of the query. Using DISTINCT is fine in the development process but not when you put it into production.

Any way here is the query that I came up with:

SELECT FV.DESCRIPTION, FV.FLEX_VALUE_MEANING
FROM FND_FLEX_VALUES_VL  FV,
     FND_ID_FLEX_SEGMENTS FIFS
WHERE FIFS.APPLICATION_ID = 101 
AND FIFS.ID_FLEX_CODE = 'GL#'
AND FIFS.FLEX_VALUE_SET_ID = FV.FLEX_VALUE_SET_ID
AND FIFS.SEGMENT_NAME = 'COMPANY'
AND FV.VALUE_CATEGORY = 'blah blah COMPANY'
AND NOT EXISTS (SELECT FV.FLEX_VALUE
                   FROM FND_FLEX_VALUE_CHILDREN_V FFVC
                    WHERE FFVC.PARENT_FLEX_VALUE = FV.FLEX_VALUE)
ORDER BY  FV.FLEX_VALUE_MEANING

The subquery eliminates the companies that are rolled up to contain more than one company.