Понимание различий между таблицей и API транзакций

Друзья,

This Ask Tom thread, который я нашел через другой вопрос SO, упоминает Table и Transactional API, и я пытаюсь понять разницу между ними,

API таблицы (TAPI) - это тот, где нет доступа к базовым таблицам, и для получения информации есть "получатели" и "сеттеры".

Например, чтобы выбрать адрес, я бы:

   the_address := get_address(address_id);

Вместо:

   select the_address
   from some_table
   where identifier = address_id

И затем, чтобы изменить адрес, я бы вызвал другой TAPI, который позаботится об изменении:

   ...
   change_address(address_id, new_address);
   ...

Transactional API (XAPI) снова находится там, где нет прямого доступа для изменения информации в таблице, но я могу выбрать из нее? (вот где мое понимание немного туманно)

Чтобы выбрать адрес, я бы:

   select the_address
   from some_table
   where identifier = address_id

а затем, чтобы изменить его, я бы назвал

   ...
   change_address(address_id, new_address);
   ...

Таким образом, единственное различие, которое я вижу между TAPI и XAPI, - это метод, в котором запись извлекается из базы данных, то есть выбор в сравнении с вызовом PL/SQL?

Это так? или я полностью потерял точку?

Ответ 1

Начнем с API таблиц. Это практика посредничества в доступе к таблицам через API PL/SQL. Итак, у нас есть пакет для таблицы, который должен быть создан из словаря данных. В пакете представлен стандартный набор процедур для выдачи DML для таблицы и некоторые функции для извлечения данных.

Для сравнения, Transactional 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;
/

В то время как с Transactional 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);

... особенно если запись Employee имеет столбцы LOB.

Он также более эффективен, чем:

l_sal := emp_utils.get_sal(p_eno);
l_comm := emp_utils.get_comm(p_eno);

... если каждый из этих геттеров выполняет отдельный оператор SELECT. Что не известно: это плохая практика OO, которая приводит к ужасной производительности базы данных.

Сторонники API таблиц утверждают их на основе того, что они защищают разработчика от необходимости думать о SQL. Люди, которые их осуждают, не любят Table API по той же причине. Даже лучшие API таблицы обычно стимулируют обработку RBAR. Если мы будем писать наш собственный SQL каждый раз, мы с большей вероятностью будем выбирать подход, основанный на наборе.

Использование Transactional APis не обязательно исключает использование функций get_resultset(). В API запросов все еще очень много. Но он скорее всего будет построен из представлений и функций, реализующих объединения, чем SELECT в отдельных таблицах.

Кстати, я думаю, что создание API транзакций поверх 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 это означает, что для записи соединения нам нужно выбрать "Сотрудники по регионам". С помощью API таблиц намного сложнее, потому что Region не является ключом к Сотрудникам.

Чтобы быть справедливым, если у нас есть 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.

Если вы хотите исследовать использование табличных API, лучшим местом для начала является Стивен Фейерштайн Quest CodeGen Utility (ранее QNXO). Это примерно так же хорошо, как генераторы TAPI, и это бесплатно.

Ответ 2

API таблицы (TAPI) - это простой API, который предоставляет основные операции CRUD для таблицы. Например, если у нас есть таблицаR MYTABLE (id INTEGER PRIMARY KEY, text VACHAR2(30)), то TAPI будет выглядеть примерно так:

package mytable_tapi is
    procedure create_rec (p_id integer, p_text varchar2);
    procedure update_rec (p_id integer, p_text varchar2);
    procedure delete_rec (p_id integer);
    function get_rec (p_id integer) returns mytable%rowtype;
end;

Когда вы используете TAPI, каждая таблица имеет TAPI, и каждая вставка, обновление и удаление проходит через TAPI.

API транзакций (XAPI) - это API, который работает на уровне транзакции, а не на отдельном уровне CRUD (хотя в некоторых случаях это будет одно и то же). Например, XAPI для обработки банковских транзакций может выглядеть примерно так:

package banking_xapi is
    procedure make_transfer (p_from_account integer, p_to_account integer,
                             p_amount number);
    ... -- other XAPI procs
end;

Процедура make_transfer может не выполнять одну вставку, обновление или удаление. Он может сделать что-то вроде этого:

procedure make_transfer (p_from_account integer, p_to_account integer,
                         p_amount number)
is
begin
    insert into transfer_details (from_account, to_account, amount)
       values (p_from_account, p_to_account, p_amount);

    update accounts set balance = balance-p_amount
    where account_no = p_from_account;

    update accounts set balance = balance+p_amount
    where account_no = p_to_account;
end;

то есть. он выполняет целую транзакцию, которая может состоять из 1 или нескольких операторов DML.

Сторонник TAPI сказал бы, что это неправильно закодировано и не должно содержать DML, но вместо этого следует вызвать код TAPI, например:

procedure make_transfer (p_from_account integer, p_to_account integer,
                         p_amount number)
is
begin
    transactions_tapi.insert_rec (p_from_account, p_to_account, p_amount);

    accounts_tapi.update_rec (p_from_account, -p_amount);

    accounts_tapi.update_rec (p_to_account, p_amount);
end;

Другие (например, Том Кит и я) увидели бы это как излишний, добавив никакой реальной ценности.

Таким образом, вы можете иметь только XAPI (Tom Kyte way) или XAPI, которые называют TAPI (способ Стив Фейерштейна). Но в некоторых системах есть только TAPI, который действительно беден - то есть они оставляют его авторам пользовательского интерфейса, чтобы объединить необходимые вызовы TAPI для создания транзакции. См. мой блог о последствиях этого подхода.