Zacznijmy od interfejsu API tabel. Jest to praktyka pośredniczenia w dostępie do tabel za pośrednictwem interfejsu API PL/SQL. Mamy więc pakiet na tabelę, który należy wygenerować ze słownika danych. Pakiet przedstawia standardowy zestaw procedur do wydawania DML względem tabeli oraz kilka funkcji do pobierania danych.
Dla porównania API transakcyjne reprezentuje jednostkę pracy. W ogóle nie ujawnia żadnych informacji o podstawowych obiektach bazy danych. Transakcyjne interfejsy API oferują lepszą enkapsulację i czystszy interfejs.
Kontrast jest taki. Rozważ te zasady biznesowe dotyczące tworzenia nowego działu:
- Nowy dział musi mieć nazwę i lokalizację
- Nowy dział musi mieć kierownika, który musi być istniejącym pracownikiem
- Inni dotychczasowi pracownicy mogą zostać przeniesieni do nowego działu
- Nowi pracownicy mogą zostać przypisani do nowego działu
- Nowy dział musi mieć przydzielonych co najmniej dwóch pracowników (w tym kierownika)
Przy użyciu interfejsów API tabel transakcja może wyglądać mniej więcej tak:
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;
/
Natomiast z API Transakcyjnym jest to znacznie prostsze:
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;
/
Skąd więc różnica w pobieraniu danych? Ponieważ podejście Transactional API odradza stosowanie ogólnych funkcji get()
funkcji, aby uniknąć bezmyślnego używania nieefektywnych instrukcji SELECT.
Na przykład, jeśli chcesz tylko wynagrodzenia i prowizji dla pracownika, odpytując to ...
select sal, comm
into l_sal, l_comm
from emp
where empno = p_eno;
... jest lepsze niż wykonanie tego ...
l_emprec := emp_utils.get_whole_row(p_eno);
...zwłaszcza jeśli rekord pracownika zawiera kolumny LOB.
Jest również bardziej wydajny niż:
l_sal := emp_utils.get_sal(p_eno);
l_comm := emp_utils.get_comm(p_eno);
... jeśli każdy z tych getterów wykonuje oddzielną instrukcję SELECT. Co nie jest nieznane:jest to zła praktyka obiektowa, która prowadzi do strasznej wydajności bazy danych.
Zwolennicy API tabel argumentują za nimi, ponieważ chronią programistę przed koniecznością myślenia o SQL. Ludzie, którzy je odrzucają, nie lubią interfejsów API tabel z tego samego powodu . Nawet najlepsze interfejsy API tabel mają tendencję do zachęcania do przetwarzania RBAR. Jeśli za każdym razem piszemy własny kod SQL, z większym prawdopodobieństwem wybierzemy podejście oparte na zbiorach.
Korzystanie z transakcyjnych interfejsów API niekoniecznie wyklucza użycie get_resultset()
Funkcje. Interfejs API zapytań nadal ma dużą wartość. Ale jest bardziej prawdopodobne, że będzie zbudowany z widoków i funkcji implementujących złączenia niż z SELECTów na poszczególnych tabelach.
Nawiasem mówiąc, uważam, że budowanie interfejsów API transakcyjnych w oparciu o interfejsy API tabel nie jest dobrym pomysłem:nadal mamy wyciszone instrukcje SQL zamiast starannie napisanych złączeń.
Jako ilustrację przedstawiamy dwie różne implementacje transakcyjnego interfejsu API do aktualizacji wynagrodzenia każdego pracownika w regionie (region jest dużą sekcją organizacji; departamenty są przypisane do regionów).
Pierwsza wersja nie ma czystego SQL, tylko wywołania Table API, nie sądzę, że jest to słomiany człowiek:używa funkcji, które widziałem w pakietach Table API (chociaż niektórzy używają dynamicznego SQL zamiast nazwanych procedur 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;
/
Równoważna implementacja w 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;
/
Jest to znacznie przyjemniejsze niż zagnieżdżone pętle kursora i aktualizacja pojedynczego wiersza w poprzedniej wersji. Dzieje się tak, ponieważ w SQL napisanie sprzężenia jest bardzo proste, musimy wybrać pracowników według regionu. Korzystanie z API tabel jest o wiele trudniejsze, ponieważ Region nie jest kluczem pracowników.
Szczerze mówiąc, jeśli mamy API tabel, które obsługuje dynamiczny SQL, sytuacja wygląda lepiej, ale nadal nie jest idealna:
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;
/
ostatnie słowo
Powiedziawszy to wszystko, istnieją scenariusze, w których interfejsy API tabel mogą być przydatne, sytuacje, w których chcemy wchodzić w interakcje tylko z pojedynczymi tabelami w dość standardowy sposób. Oczywistym przypadkiem może być produkcja lub konsumpcja strumieni danych z innych systemów, np. ETL.
Jeśli chcesz zbadać użycie interfejsów API tabel, najlepszym miejscem do rozpoczęcia jest Quest CodeGen Utility Stevena Feuersteina (dawniej QNXO). To jest tak dobre, jak generatory TAPI i jest bezpłatne.