Oracle
 sql >> база данни >  >> RDS >> Oracle

Разбиране на разликите между API за таблици и транзакции

Нека започнем с API за таблица. Това е практиката на посредничество за достъп до таблици чрез PL/SQL API. И така, имаме пакет за таблица, който трябва да бъде генериран от речника с данни. Пакетът представя стандартен набор от процедури за издаване на DML срещу таблицата и някои функции за извличане на данни.

За сравнение транзакционният API представлява единица работа. Той изобщо не разкрива никаква информация за основните обекти на базата данни. Транзакционните API предлагат по-добро капсулиране и по-чист интерфейс.

Контрастът е такъв. Помислете за тези бизнес правила за създаване на нов отдел:

  1. Новият отдел трябва да има име и местоположение
  2. Новият отдел трябва да има мениджър, който трябва да бъде съществуващ служител
  3. Други съществуващи служители могат да бъдат прехвърлени в новия отдел
  4. Нови служители могат да бъдат назначени в новия отдел
  5. Новият отдел трябва да има поне двама назначени служители (включително мениджъра)

При използване на 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);

...особено ако записът на служител има LOB колони.

Освен това е по-ефективен от:

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

... ако всеки от тези получатели изпълнява отделен оператор SELECT. Което не е неизвестно:това е лоша OO практика, която води до ужасна производителност на базата данни.

Поддръжниците на приложните програмни интерфейси (API) за таблици се аргументират за тях на базата на това, че те предпазват разработчика от необходимостта да мисли за SQL. Хората, които ги отхвърлят, не харесват API за таблици по същата причина . Дори най-добрите API за таблици са склонни да насърчават обработката на RBAR. Ако всеки път пишем собствен SQL, е по-вероятно да изберем подход, базиран на набори.

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

Между другото, мисля, че изграждането на транзакционни API върху API на таблици не е добра идея:все още имаме затворени SQL изрази вместо внимателно написани присъединявания.

Като илюстрация, ето две различни реализации на транзакционен API за актуализиране на заплатата на всеки служител в даден регион (регионът е голяма част от организацията; отделите са присвоени на региони).

Първата версия няма чист SQL, а просто извиквания на Table API, не мисля, че това е сламка:тя използва вида функционалност, която съм виждал в пакетите 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 за таблица, тъй като регионът не е ключ за служителите.

За да бъдем честни, ако имаме 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 на Steven Feuerstein (преди QNXO). Това е приблизително толкова добро, колкото получават генераторите на TAPI, и е безплатно.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Oracle Database BLOB към InputStream в Java?

  2. Пример за съхранена процедура в Oracle с параметър IN OUT

  3. Как да попълните набора от данни с C# от базата данни на Oracle

  4. какво представлява материализираните възгледи в Oracle?

  5. Oracle - Как да създадете потребител само за четене