Question

I have two packages pkg_company and pkg_employee. pkg_company includes a (record) type definition typ_company. pkg_employee includes a type definition typ_employee.

I have one instance of company type and 1000s of instances of typ_employee. In the employee type, I would like to have a pointer to the company instance.

declare
    c pkg_company.typ_company;
    e pkg_employee.typ_employee;
begin
  c := pkg_company.get(12345);
  for e in (select employeeid from employee where companyid=12345)
  loop
    e := pkg_employee.get(12345, c);   -- c passed by reference in out nocopy
    pkg_employee.process(e);  -- I would like to access company info inside here as e.c.companyname
  end loop;  
end;

How do I store a pointer to c inside e? I don't want to create 1000s of copies of c. Just want to store the pointer and access the value when needed.

Thanks for all the help!

end;

Was it helpful?

Solution

In Oracle SQL it's possible to use REF and DEREF functions to pass logical pointers to objects stored in database tables.
But there are no such thing as pointer or reference in PL/SQL so you need a workaround.

Below some approaches for possible workarounds in PL/SQL. I apologize in advance for any errors in the code. It's intended rather to demonstrate approaches than for use in production.

Approach 1 - cached access method

Generic idea is to access entities through function which cache results:

create or replace package EntitytAccess as

  procedure GetCompany1(
    pCompanyId in         number, 
    pCompany   out nocopy pkg_company.typ_company 
  ); 

  function GetCompany2(
    pCompanyId in number
  ) return pkg_company.typ_company;

  procedure ClearCompanyCache;

end;

Access package body:

create or replace package body EntitytAccess as

  type typ_company_cache is table of pkg_company.typ_company index by number;

  var_company_cache typ_company_cache;

  procedure GetCompany1(
    pCompanyId in         number, 
    pCompany   out nocopy pkg_company.typ_company 
  )
  is           
  begin
    if( var_company_cache.exists(pCompanyId) ) 
      pCompany := var_company_cache(pCompanyId);
    else
      pCompany := pkg_company.get(pCompanyId);
      var_company_cache(pCompanyId) := pCompany;
    end if; 
  end;

  function GetCompany2(
    pCompanyId in number
  ) return pkg_company.typ_company
  is       
  begin
    if(not var_company_cache.exists(pCompanyId) ) 
      var_company_cache(pCompanyId) := pkg_company.get(pCompanyId);
    end if;                               
    return  var_company_cache(pCompanyId);
  end;

  procedure ClearCompanyCache
  is
  begin              
    var_company_cache.Delete;
  end;

end;

Usage:

declare
  c pkg_company.typ_company;
  e pkg_employee.typ_employee;
begin
  EntityAccess.GetCompany2(12345,c);
  for e in (select employeeid from employee where companyid=12345)
  loop

    e := pkg_employee.get(12345, c); -- c passed by reference in out nocopy
                                     -- and c.companyid stored inside e.

    pkg_employee.process(e);  -- No need to pass company, but inside process() 
                              -- method you need to call either 
                              -- EntityAccess.GetCompany1(e.companyid, var_c)
                              -- or
                              -- var_c := EntityAccess.GetCompany2(e.companyid)

  end loop;  
end;

Approach 2 - environment package

Because package state belongs to one session you can use package variables to hold current processing state and reference it when needed.

create or replace package ProcessEnvironment as

  var_current_company pkg_company.typ_company;

  -- may be more "global" variables here
end;
/

create or replace package body ProcessEnvironment as
end;

Usage:

declare
  e pkg_employee.typ_employee;
begin
  ProcessEnvironmant.var_current_company := pkg_company.get(12345);

  for e in (select employeeid from employee where companyid=12345)
  loop

    e := pkg_employee.get(12345, ProcessEnvironmant.var_current_company); 
                            -- c passed by reference in out nocopy
                            -- and c.companyid stored inside e.

    pkg_employee.process(e);  -- No need to pass company, but inside process() 
                              -- method you references
                              -- ProcessEnvironmant.var_current_company 
                              -- with appropriate checks

  end loop;  
end;

Mixed approach

Seems that in case of Approach 1 instances copied from collection, especially if accessed with function GetCompany2(). Copy constructor may be fast enough but produce some overhead.
For Approach 2 there are some checks must be present in code of business logic function so it's maintenance overhead just from another point of view.
To deal with both problems you can use cache, but hold only one value inside a package:

create or replace package EntitytAccess as

  procedure GetCompany(
    pCompanyId in         number, 
    pCompany   out nocopy pkg_company.typ_company 
  ); 

end;

Package body:

create or replace package body EntitytAccess as

  var_cached_company pkg_company.typ_company;

  procedure GetCompany(
    pCompanyId in         number, 
    pCompany   out nocopy pkg_company.typ_company 
  )
  is           
  begin

    if( (var_cached_company is null) 
        or 
        (var_cached_company.comanyid != pCompanyId)
      )then

      var_company_cache := pkg_company.get(pCompanyId);

    end if; 

    pCompany := var_cached_company;

  end;

end;

Usage:

declare
  c pkg_company.typ_company;
  e pkg_employee.typ_employee;
begin
  EntityAccess.GetCompany(12345,c);

  for e in (select employeeid from employee where companyid= c.companyid)
  loop

    e := pkg_employee.get(c.companyid , c); -- c passed by reference in out nocopy
                                            -- and c.companyid stored inside e.

    pkg_employee.process(e);  -- No need to pass company, but inside process() 
                              -- method you need to call 
                              -- EntityAccess.GetCompany(e.companyid, var_c)
                              -- where var_c is company object variable.

  end loop;  
end;

OTHER TIPS

How do I store a pointer to c inside e? I don't want to create 1000s of copies of c. Just want to store the pointer and access the value when needed.

"pkg_employee.get" is a function - remove OUT parameters.

  1. It is bad practice to define OUT parameters for a function.
  2. Why is it out parameter if you work only with the function's result?

If you call the same function over and over again and the function is deterministic as performance boost you can use function's "RESULT_CACHE" clause. Please read about it first to be sure that this is what you need.

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