Oracle Pl/SQL: как реализовать указатели на типы записей в памяти
У меня есть два пакета pkg_company и pkg_employee. pkg_company включает определение типа (запись) typ_company. pkg_employee включает определение типа typ_employee.
У меня есть один экземпляр типа компании и тысячи экземпляров typ_employee. В типе сотрудника я хотел бы иметь указатель на экземпляр компании.
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;
Как мне сохранить указатель на c внутри e? Я не хочу создавать тысячи экземпляров c. Просто хотите сохранить указатель и получить доступ к значению при необходимости.
Спасибо за помощь!
конец;
2 ответа
В Oracle SQL
можно использовать REF
а также DEREF
функции для передачи логических указателей на объекты, хранящиеся в таблицах базы данных.
Но нет такой вещи, как указатель или ссылка в PL/SQL
так что вам нужен обходной путь.
Ниже приведены некоторые подходы для возможных обходных путей в PL/SQL. Я заранее прошу прощения за любые ошибки в коде. Он предназначен скорее для демонстрации подходов, чем для использования в производстве.
Подход 1 - метод кэшированного доступа
Общая идея состоит в том, чтобы получить доступ к объектам через функцию, которая кеширует результаты:
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;
Тело пакета доступа:
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;
Использование:
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;
Подход 2 - пакет среды
Поскольку состояние пакета принадлежит одному сеансу, вы можете использовать переменные пакета для хранения текущего состояния обработки и ссылки на него при необходимости.
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;
Использование:
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;
Смешанный подход
Кажется, что в случае Approach 1
экземпляры, скопированные из коллекции, особенно если доступ осуществляется с помощью функции GetCompany2()
, Конструктор копирования может быть достаточно быстрым, но при этом возникают некоторые накладные расходы
За Approach 2
в коде функции бизнес-логики должны присутствовать некоторые проверки, так что это накладные расходы на обслуживание просто с другой точки зрения.
Для решения обеих проблем вы можете использовать кеш, но хранить только одно значение внутри пакета:
create or replace package EntitytAccess as
procedure GetCompany(
pCompanyId in number,
pCompany out nocopy pkg_company.typ_company
);
end;
Тело пакета:
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;
Использование:
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;
Как мне сохранить указатель на c внутри e? Я не хочу создавать тысячи экземпляров c. Просто хотите сохранить указатель и получить доступ к значению при необходимости.
"pkg_employee.get" - это функция - удалить параметры OUT.
- Неправильно определять параметры OUT для функции.
- Почему это выходной параметр, если вы работаете только с результатом функции?
Если вы вызываете одну и ту же функцию снова и снова, и эта функция является детерминированной для повышения производительности, вы можете использовать функцию RESULT_CACHE. Пожалуйста, прочитайте об этом в первую очередь, чтобы убедиться, что это то, что вам нужно.