Давайте начнем с Table API. Это практика передачи доступа к таблицам через PL / SQL API. Итак, у нас есть пакет для каждой таблицы, который должен быть сгенерирован из словаря данных. В пакете представлен стандартный набор процедур выдачи DML для таблицы и некоторые функции для извлечения данных.
Для сравнения транзакционный API представляет собой единицу работы. Он вообще не предоставляет никакой информации о базовых объектах базы данных. Транзакционные API предлагают лучшую инкапсуляцию и более чистый интерфейс.
Контраст такой. Рассмотрим следующие бизнес-правила для создания нового отдела:
- Новый департамент должен иметь название и местоположение
- В новом отделе должен быть менеджер, который должен быть действующим сотрудником
- Другие существующие сотрудники могут быть переведены в новый отдел
- Новые сотрудники могут быть назначены в новый отдел
- В новом департаменте должно быть назначено как минимум два сотрудника (включая менеджера)
При использовании API таблиц транзакция может выглядеть примерно так:
DECLARE
dno pls_integer;
emp_count pls_integer;
BEGIN
dept_utils.insert_one_rec(:new_name, :new_loc, dno);
emp_utils.update_one_rec(:new_mgr_no ,p_job=>'MGR’ ,p_deptno=>dno);
emp_utils.update_multi_recs(:transfer_emp_array, p_deptno=>dno);
FOR idx IN :new_hires_array.FIRST..:new_hires_array.LAST LOOP
:new_hires_array(idx).deptno := dno;
END LOOP;
emp_utils.insert_multi_recs(:new_hires_array);
emp_count := emp_utils.get_count(p_deptno=>dno);
IF emp_count < 2 THEN
raise_application_error(-20000, ‘Not enough employees’);
END IF;
END;
/
Принимая во внимание, что с Транзакционным API это намного проще:
DECLARE
dno subtype_pkg.deptno;
BEGIN
dept_txns.create_new_dept(:new_name
, :new_loc
, :new_mgr_no
, :transfer_emps_array
, :new_hires_array
, dno);
END;
/
Так в чем же разница при получении данных? Поскольку подход Transactional API препятствует использованию общих функций get()
, чтобы избежать бессмысленного использования неэффективных операторов SELECT.
Например, если вы просто хотите получить зарплату и комиссию за сотрудника, запросите это ...
select sal, comm
into l_sal, l_comm
from emp
where empno = p_eno;
... лучше, чем выполнить это ...
l_emprec := emp_utils.get_whole_row(p_eno);
... особенно если запись сотрудника имеет столбцы больших объектов.
Это также более эффективно, чем:
l_sal := emp_utils.get_sal(p_eno);
l_comm := emp_utils.get_comm(p_eno);
... если каждый из этих получателей выполняет отдельную инструкцию SELECT. Что не является неизвестным: это плохая ОО практика, которая приводит к ужасной производительности базы данных.
Сторонники API таблиц отстаивают их на основании того, что они защищают разработчика от необходимости думать о SQL. Люди, которые осуждают их, не любят API таблиц по той же причине . Даже лучшие API таблиц имеют тенденцию поощрять обработку RBAR. Если мы пишем собственный SQL каждый раз, мы с большей вероятностью выберем подход, основанный на множествах.
Использование транзакционных AP не обязательно исключает использование get_resultset()
функций. API запросов по-прежнему имеет большую ценность. Но скорее всего он будет построен из представлений и функций, реализующих объединения, чем из SELECT для отдельных таблиц.
Между прочим, я думаю, что создание Transactional API поверх Table API не является хорошей идеей: у нас все еще есть отдельные операторы SQL вместо тщательно написанных объединений.
В качестве иллюстрации ниже приведены две различные реализации транзакционного API для обновления заработной платы каждого сотрудника в регионе (регион является крупномасштабным подразделением организации; отделы назначаются регионам).
Первая версия не имеет чистого SQL, только вызовы Table API, я не думаю, что это соломенный человек: она использует ту функциональность, которую я видел в пакетах Table API (хотя некоторые используют динамический SQL вместо имени SET_XXX ) процедуры).
create or replace procedure adjust_sal_by_region
(p_region in dept.region%type
, p_sal_adjustment in number )
as
emps_rc sys_refcursor;
emp_rec emp%rowtype;
depts_rc sys_refcursor;
dept_rec dept%rowtype;
begin
depts_rc := dept_utils.get_depts_by_region(p_region);
<< depts >>
loop
fetch depts_rc into dept_rec;
exit when depts_rc%notfound;
emps_rc := emp_utils.get_emps_by_dept(dept_rec.deptno);
<< emps >>
loop
fetch emps_rc into emp_rec;
exit when emps_rc%notfound;
emp_rec.sal := emp_rec.sal * p_sal_adjustment;
emp_utils.set_sal(emp_rec.empno, emp_rec.sal);
end loop emps;
end loop depts;
end adjust_sal_by_region;
/
Эквивалентная реализация в SQL:
create or replace procedure adjust_sal_by_region
(p_region in dept.region%type
, p_sal_adjustment in number )
as
begin
update emp e
set e.sal = e.sal * p_sal_adjustment
where e.deptno in ( select d.deptno
from dept d
where d.region = p_region );
end adjust_sal_by_region;
/
Это намного лучше, чем вложенные циклы курсора и однострочное обновление предыдущей версии. Это связано с тем, что в SQL проще всего написать объединение, в котором нам нужно выбрать сотрудников по регионам. Использование Table API намного сложнее, потому что Region не является ключевым для сотрудников.
Если честно, если у нас есть Table API, который поддерживает динамический SQL, все будет лучше, но все же не идеально:
create or replace procedure adjust_sal_by_region
(p_region in dept.region%type
, p_sal_adjustment in number )
as
emps_rc sys_refcursor;
emp_rec emp%rowtype;
begin
emps_rc := emp_utils.get_all_emps(
p_where_clause=>'deptno in ( select d.deptno
from dept d where d.region = '||p_region||' )' );
<< emps >>
loop
fetch emps_rc into emp_rec;
exit when emps_rc%notfound;
emp_rec.sal := emp_rec.sal * p_sal_adjustment;
emp_utils.set_sal(emp_rec.empno, emp_rec.sal);
end loop emps;
end adjust_sal_by_region;
/
последнее слово
Сказав все это, есть сценарии, в которых API-интерфейсы таблиц могут быть полезны, когда мы хотим взаимодействовать с отдельными таблицами только стандартными способами.Очевидным случаем может быть создание или использование потоков данных из других систем, например ETL.
Если вы хотите исследовать использование Table APIs, лучше всего начать с Quest CodeGen Utility Стивена Фюрштайна (ранее QNXO).Это почти так же хорошо, как генераторы TAPI, и это бесплатно.