Tranzacţii

O tranzacţie poate fi considerată ca o succesiune de instrucţiuni SQL executate de un singur utilizator (instrucţiuni de consultare şi modificare a bazei de date). Execuţia operaţiilor de modificare a unei baze de date trebuie controlată deoarece există unele succesiuni de operaţii care trebuie să fie executate obligatoriu împreună pentru a se păstra consistenţa bazei de date (ex. transferul unei cantităţi monetare dintr-un cont în alt cont). Serverul de date trebuie să garanteze că execuţia intrucţiunilor grupate într-o tranzacţie se poate realiza în întregime. Dacă cel putin una din operaţii nu se poate executa, atunci trebuie anulate toate operaţiile din tranzacţie.

După execuţia întregii liste de operaţii dintr-o tranzacţie trebuie precizat că operaţiile de modificare trebuie anulate sau trebuie păstrate.

Fiecare server de date trebuie să aibă şi facilităţi de recuperare la erorile care pot să afecteze baza de date.

Tipuri de erori:

Rolul tranzacţiilor:

O tranzacţie trebuie să respecte cele patru proprietăţi (ACID):

  1. Atomică - şirul de modificări efectuate într-o tranzacţie este indivizibil, deci aceste modificări (incluse în tranzacţie) fie sunt executate împreună, fie sunt anulate (se comportă ca o singură operaţie).
  2. Consistentă - modificările din tranzacţie preiau o bază de date consistentă şi o predau, la final, tot într-o stare consistentă (sunt respectate toate restricţiile definite pentru baza de date).
  3. Izolată - fiecare tranzacţie se execută independent (operaţiile de modificare dintr-o tranzacţie nu pot fi intercalate cu alte operaţii de modificare a aceloraşi date din baza de date, operaţii aflate într-o altă tranzacţie). Ca o consecinţă, modificările efectuate într-o tranzacţie nu depind de altă tranzacţie, deci două tranzacţii asupra aceloraşi date se execută serial.
  4. Durabilă - după finalizarea unei tranzacţii şi păstrarea modificărilor, acestea devin permanente, chiar dacă are loc o "cădere" a sistemului (serverul de date trebuie să recupereze aceste modificări).

O modalitate de a respecta proprietăţile tranzacţiilor, în cazul unui acces concurent, este de a construi un plan de execuţie echivalent cu un plan neconcurent (care ar corespunde la o execuţie "serială" a tranzacţiilor. Un astfel de plan presupune că după ce o tranzacţie a devenit activă, deci a început să se execute prima operaţie din aceasta, celelalte tranzacţii cerute spre execuţie nu pot fi executate (sunt trecute într-o stare de aşteptare până la terminarea primei tranzacţii).

Deoarece multe tranzacţii actionează asupra unor date diferite, serverele de date permit intercalarea operaţiilor de gestiune a datelor, chiar dacă se efectuează din tranzacţii diferite. Această posibilitate de lucru creşte numărul de tranzacţii efectuate într-o anumită perioadă de timp prin folosirea resurselor serverului în timp ce o tranzacţie aflată în execuţie necesită date de pe suport. Pentru a păstra consistenţa bazei de date, un astfel de plan de execuţie în paralel a mai multor tranzacţii trebuie să fie echivalent cu unul serial (care este corect). Crearea unui astfel de plan se face de un planificator (scheduler) a operaţiilor de modificare a bazei de date.

Două operaţii asupra bazei de date (citire, modificare), din tranzacţii diferite, intră în conflict dacă vizează aceeaşi dată, din care cel puţin una din operaţii este o operaţie de modificare. Pentru a elimina astfel de conflicte este necesar un mecanism pentru asigurarea izolării. Un astfel de mecanism se poate realiza prin blocarea datelor, care (în mare) este:

Pentru a se asigura serializabilitatea (realizarea unui plan de execuţie echivalent cu unul serial, deci un plan de execuţie corect) este necesar ca tranzacţiile să respecte protocolul de blocare în două faze (într-o prima fază se fac numai blocări, după care urmează deblocări de date). Cerinţele acestui protocol trebuie respectate de aplicaţii. Acest protocol nu asigură că două tranzacţii generează impas.

Nivelele de izolare a tranzacţiilor

Mecanismul de blocare amintit (pentru a asigura serializabilitatea), şi care păstrează consistenţa datelor, este foarte restrictiv (dacă sunt multe tranzacţii concurente, atunci multe vor fi în starea de aşteptare). Pentru a asigura un acces mai mare la date (care să crească numărul de tranzacţii care se execută), multe SGBD-uri oferă anumite nivele de izolare pentru operaţiile de acces la date (citire, scriere), deci se slăbesc regulile de blocare. Cu aceste nivele mai scăzute de izolare pot apare unele probleme (fenomene):

In standardul SQL sunt definite patru nivele de izolare:

Posibilele anomalii care apar pentru diversele nivele de izolare sunt marcate cu "+" în tableul următor:

Nivele de izolare/fenomenecitirile inconsistentecitirile nerepetitivecitirile fantomă
Citiri nesalvate+++
Citiri salvate-++
Citiri repetabile--+
Seriabilitate---

Nivele de izolare în Oracle

La sistemul Oracle se pot utiliza următoarele nivele de izolare:

Precizarea nivelului de izolare a unei tranzacţii Oracle se face la începutul acesteia prin una din instructiunile:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET TRANSACTION READ ONLY;

Nivelul de izolare a unei tranzacţii se poate modifica, în timpul execuţiei, prin una din instrucţiunile:

ALTER SESSION SET ISOLATION_LEVEL=SERIALIZABLE;
ALTER SESSION SET ISOLATION_LEVEL=READ COMMITTED;

Se observă că nu se poate face schimbarea nivelului de izolare la READ ONLY în timpul execuţiei tranzacţiei.

Crearea tranzacţiilor Oracle

In SQL există două comenzi care pot să fie folosite pentru a preciza sfârşitul unei tranzacţii:

O tranzacţie începe automat la execuţia primei instrucţiuni SQL care se execută, sau a unei instrucţiuni SET TRANSACTION, şi se termină prin una din instrucţiunile precedente.
Intr-o tranzacţie nu se pot folosi două sau mai multe instrucţiuni SET TRANSACTION.

După precizarea unui mod de terminare a tranzacţiei, prin una din cele două modalităţi, începe o nouă tranzacţie prin acelaşi procedeu.

La sfârşitul normal al unei sesiuni de lucru se execută implicit un "commit", iar dacă sesiunea se termină anormal, atunci modificările efectuate în ultima tranzacţie se pierd, deci se execută un rollback.
La nivel de SQL*Plus putem folosi comanda:

set autocommit {on | off | nr}

pentru a preciza că după fiecare operaţie de modificare se face automat un commit sau nu, sau numărul de modificări după care se face un commit automat.

Dacă există multe instrucţiuni sql într-o tranzacţie, atunci se pot pune marcaje intermediare (savepoints), care se declară. In acest fel se poate diviza o tranzacţie în părţi mai mici. Se poate anula efectul instrucţiunilor de modificare din întreaga tranzacţie, sau până la un anumit punct de salvare. Reluarea execuţiei instrucţiunilor se poate face de la un astfel de punct de reluare. Instrucţiunea SQL:

savepoint nume

va marca un astfel de punct de referinţă în lista de eliminări ale modificarilor dintr-o tranzacţie. Pentru utilizarea unei astfel de referinţe avem instrucţiunea:

roolback [work] to [savepoint] nume

care elimină modificările efectuate de la punctul de referinţă menţionat şi şterge punctele de referinţă definite după acesta.
Instrucţiunea commit şterge toate punctele de referinţă.

Din cele precizate mai sus rezultă următoarele operaţii care se fac la execuţia instrucţiunii COMMIT:

Modificările efectuate într-o tranzacţie sunt păstrate în diverse buffere ale serverului (deci modificările sunt temporare). Aceste modificări se salvează efectiv în baza de date la execuţia unei instrucţiuni COMMIT (după care datele se şterg din buffere şi refacerea unor date anterioare nu mai este posibilă). Stergerea acestor buffere se face şi la executarea instructiunii ROLLBACK. Prin acest mod de păstrare a modificărilor se asigură:

Observaţie. Instrucţiunile ROLLBACK, COMMIT şi SAVEPOINT nu pot fi folosite în triggere.

Exemple

Considerăm tabelul:
create table stud(nume varchar2(30), prenume varchar2(30), cnp char(13));

Exemplul 1:

insert into stud select nume,prenume,cnp from studenti where substr(cnp,2,2)='89';
commit;
/* adaugarea inregistrarilor este pastrata in baza de date */
select count(*) from stud;  
/* va furniza numarul de inregistrari adaugate */
delete from stud;
select count(*) from stud;  
/* nu exista inregistrari */
commit;
select count(*) from stud;  
/* stergerile se pastreaza */

Exemplul 2:

delete from stud;
insert into stud select nume,prenume,cnp from studenti where substr(cnp,2,2)='89';
commit;
select count(*) from stud;
/* va furniza numarul de inregistrari existente */
delete from stud;
select count(*) from stud;
/* nu exista inregistrari */
rollback;
select count(*) from stud;
/* stergerile se anuleaza, se da numarul de inregistrari din baza de date */

Exemplul 3, apariţie "Nonrepeatable Read" şi "Phantom Read" (implicit apare nivelul de izolare READ COMMITTED):

Tranzacţia T1Tranzacţia T2
delete from stud;
insert into stud select nume,prenume,cnp from studenti where substr(cnp,2,2)='89';
commit;
select * from stud where cnp='2890113420002';
/* se obtin datele pentru 's1' */
 
 
update stud set nume='Pop' where cnp='2890113420002';
insert into stud(nume,prenume,cnp) values('Pop','Ion','1');
commit;
/* se actualizeaza datele pentru 's1' la 's2', se adauga 's3' */
select * from stud where cnp='2890113420002';
/* se obtin datele pentru 's2', in aceeasi tranzactie, cu aceeasi interogare */
/* cu o interogare:
select * from stud
se obtine o inregistrare noua */
 

O tranzacţie se poate defini ca read-only. In acest caz instrucţiuni INSERT, UPDATE, DELETE şi SELECT cu FOR UPDATE nu sunt permise.

Exemplul 4:

commit;
SET TRANSACTION READ ONLY ;
/* tipul tranzactiei */
SELECT COUNT(*) FROM stud;
/* se furnizeaza valoarea */
UPDATE stud SET nume='Popescu' WHERE cnp='2';
/* ORA-01456: may not perform insert/delete/update operation inside a READ ONLY transaction */

Exemplul 5:

commit;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
declare
  n char(30);
begin
  delete from stud where cnp in ('3','4');
  insert into stud select nume,prenume,cnp from studenti where substr(cnp,2,2)='90';
  select nume into n from stud;
end;
/
select * from stud;

La final se furnizează toate datele din tabelul stud, deoarece blocul produce o eroare (cu instrucţiunea select se obţin mai multe înregistrări) şi se generează rollback, deci efectul instrucţiunilor delete şi insert este anulat.

Exemplul 6:

Tranzacţia T1Tranzacţia T2
delete from stud;
INSERT INTO stud(cnp,nume,prenume) VALUES ('1','Aa','Bb');
INSERT INTO stud(cnp,nume,prenume) VALUES ('2','Xx','Yy');
commit;
update stud set prenume='Cc' where cnp='1';
select * from stud;
--rezultat:
NUME                 PRENUME              CNP         
-------------------- -------------------- -------------
Aa                   Cc                   1             
Xx                   Yy                   2 
 
 
update stud set prenume='Zz' where cnp='2';
select * from stud;
--rezultat:
NUME                 PRENUME              CNP         
-------------------- -------------------- -------------
Aa                   Bb                   1             
Xx                   Zz                   2
update stud set prenume='Uu' where cnp='2';
-- se asteapta deblocarea inregistrarii
 
 
update stud set prenume='Vv' where cnp='1';
-- se asteapta deblocarea inregistrarii
SQL Error: ORA-00060: deadlock detected while waiting for resource
00060. 00000 -  "deadlock detected while waiting for resource"
*Cause:    Transactions deadlocked one another while waiting for resources.
*Action:   Look at the trace file to see the transactions and resources
           involved. Retry if necessary.
 
-- trebuie anulate modificarile 
--    deoarece unele nu s-au executat
ROLLBACK;
 
 
update se executa
select * from stud;
--rezultat:
NUME                 PRENUME              CNP         
-------------------- -------------------- -------------
Aa                   Vv                   1             
Xx                   Zz                   2
commit;
select * from stud;
--rezultat:
NUME                 PRENUME              CNP         
-------------------- -------------------- -------------
Aa                   Bb                   1             
Xx                   Yy                   2 
 
 
commit;
select * from stud;
--rezultat:
NUME                 PRENUME              CNP         
-------------------- -------------------- -------------
Aa                   Vv                   1             
Xx                   Zz                   2
 

Serverul Oracle asigură consistenţa datelor la citire în timpul execuţiei unei instrucţiuni SQL, adică se garantează că toate datele regăsite de o interogare provin dintr-un singur moment al existenţei bazei de date, deci datele sunt consistente. Această afirmaţie este adevărată şi pentru subinterogările incluse în instrucţiunea sql.

Alegerea nivelului de izolare în aplicaţii

  1. Tranzacţiile serializabile nu se recomandă în cazul în care acestea cer un timp mare de executie deoarece e posibil ca multe tranzacţii să fie blocate sau să fie anulate din cauza impasului.
  2. Dacă nivelul de concurenţă este mai mic, atunci nivelul de izolare serializabil este cel mai avantajos deoarece asigură consistenţa datelor şi nu apar fenomenele "citirile nerepetitive" şi "citirile fantomă".
  3. Deoarece multe tranzacţii au timpul de execuţie mic (cerinţă care se recomandă), se poate folosi nivelul de izolare read committed (deoarece se asigură execuţia concurentă a multor tranzacţii, chiar dacă cele două fenomene amintite pot să apară). Din această cauză nivelul de izolare read committed este cel implicit în Oracle.

Tipuri de blocaje în Oracle

In afara facilităţilor de stabilire a unui nivel de izolare la tranzacţii, serverul Oracle permite şi blocări explicite, la nivel de înregistrare sau la nivel de tabel, pentru a controla mai bine execuţia tranzacţilor concurente. Sarcina alegerii blocajelor necesare (în tranzacţii) cade în sarcina dezvoltatorilor de aplicaţii.
Blocările explicite se realizează prin instrucţiunea:

LOCK TABLE tabel IN tip MODE [{NOWAIT | WAIT intreg}]

Cu WAIT intreg se precizează că se poate aştepta un număr precizat de secunde pentru realizarea blocării, iar cu NOWAIT se precizează că nu se aşteaptă dacă blocarea cerută nu se poate realiza. Dacă blocarea nu se poate executa, atunci se generează o eroare. Fără WAIT sau NOWAIT se aşteaptă până în momentul în care operaţia de blocare se poate realiza.

In Oracle se folosesc două moduri de blocare:

Blocările sunt utile la execuţia instrucţiunilor:

Blocarea poate fi făcută la nivel de:

Asa cum s-a precizat, respectarea protocolului de blocare în două faze nu elimină apariţia impasului (deadlock). Serverul Oracle detectează situaţiile de impas şi anulează una din tranzacţii (face un rollback). In plus, trimite un mesaj de eroare la client.

Tipurile de blocare sunt:

(RS):  LOCK TABLE tabel IN ROW SHARE MODE;
(RX):  LOCK TABLE tabel IN ROW EXCLUSIVE MODE;
(S):   LOCK TABLE tabel IN SHARE MODE;
(SRX): LOCK TABLE tabel IN SHARE ROW EXCLUSIVE MODE;
(X):   LOCK TABLE tabel IN EXCLUSIVE MODE;
  1. Partajat la nivel de înregistrare (Row Share - RS): cere blocarea uneia sau a mai multor înregistrări în mod partajat, pentru modificarea lor.
    Alte tranzacţii pot interoga orice înregistrare şi pot insera, modifica, şterge înregistrări concurente din acelaşi tabel, dar nu pot bloca exclusiv tabelul (modul X).
    Acest tip de blocare se realizează explicit prin instrucţiunea:
    LOCK TABLE tabel IN ROW SHARE MODE;
    

    sau implicit prin utilizarea unui cursor cu clauza FOR UPDATE, sub forma::

    CURSOR nume IS
       instructiune_select
          FOR UPDATE [OF lista_coloane] [NOWAIT];
    

    Inregistrările regăsite în cursor se pot modifica sau şterge cu instrucţiuni de forma:

    update tabel set ... where current of cursor;
    delete from tabel where current of cursor;

    La o folosire a cursorului se face şi blocarea înregistrărilor ce vor fi regăsite de cursor (şi care se vor modifica). Se continuă procedura dacă se pot realiza blocările, altfel se aşteaptă sau se generează o eroare. Deblocarea se face la finalul tranzacţiei.

    Exemplul 7:

    Tranzacţia T1Tranzacţia T2
    -- se foloseste tabelul
    --  create table stud(nume varchar2(30), prenume varchar2(30), cnp char(13));
    delete from stud;
    INSERT INTO stud(cnp,nume,prenume) VALUES ('1','Aa','Bb');
    INSERT INTO stud(cnp,nume,prenume) VALUES ('2','Xx','Yy');
    commit;
    select * from stud where cnp in ('1','2') for update nowait;
    -- se realizeaza blocarea celor doua inregistrari
    
    -- rezultat
    NUME                 PRENUME              CNP         
    -------------------- -------------------- -------------
    Aa                   Bb                   1             
    Xx                   Yy                   2 
    
     
     
    select * from stud;
    -- inregistrarile blocate de T1 se pot citi
    
    -- rezultat
    NUME                 PRENUME              CNP         
    -------------------- -------------------- -------------
    Aa                   Bb                   1             
    Xx                   Yy                   2 
    
    update stud set prenume='Dd' where cnp='1';
    -- actualizarea este in asteptare, inregistrarea este inca blocata
    
    update stud set prenume='Cc' where cnp='1';
    -- actualizarea din T1 se face
    commit;
    
     
     
    -- actualizarea din T2 se face
    -- tranzactia nu e terminata, o inreg. e blocata
    
    select * from stud where cnp in ('1','2') for update nowait;
    
    -- apare mesajul:
    SQL Error: ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired
    00054. 00000 -  "resource busy and acquire with NOWAIT specified"
    *Cause:    Resource interested is busy.
    *Action:   Retry if necessary.
    
     
     
    commit;
    -- tranzactia se termina, se face deblocarea
    
    Deoarece prin "select for update" se face blocarea tuturor înregistrărilor precizate înainte de actualizare, se consideră că această variantă de lucru este blocarea pesimistă.
    Spre deosebire de această variantă, blocarea optimistă presupune că datele care se doresc modificate nu generează conflicte, deci nu sunt necesare blocări, iar dacă unele modificări nu se pot realiza, atunci întreaga tranzacţie se anulează şi se reia execuţia ei.
  2. Exclusiv la nivel de înregistrare (Row Exclusive - RX): cere blocarea unor înregistrări, în mod exclusiv, pentru modificarea lor. Blocarea se face la execuţia instrucţiunii de modificare.

    Alte tranzacţii pot să efectueze operaţii de citire sau modificare asupra altor înregistrări care nu s-au modificat, dar nu pot să facă o blocare: S, SRX, X (deci este mai restrictivă ca ROW SHARE).
    Acest tip de blocare se realizează implicit prin una din instrucţiunile:

    INSERT INTO  ...;
    UPDATE ... ;
    DELETE FROM ...;
    

    sau explicit prin:

    LOCK TABLE tabel IN ROW EXCLUSIVE MODE;
    

  3. Partajat la nivel de tabel (Share - S), care se realizează explicit prin instrucţiunea:
    LOCK TABLE tabel IN SHARE MODE;
    

    Alte tranzacţii pot să consulte date din tabel, sau să facă o blocare partajată la nivel de linie (implicită sau explicită), sau o blocare de tip SHARE, dar nu pot să facă actualizări. Dacă mai multe tranzacţii au obţinut acest tip de blocare asupra unui tabel, atunci nici una nu va putea executa operaţii de modificare. Intr-o astfel de tranzacţie se pot face modificări numai dacă nu mai există alte tranzacţii cu acelaşi tip de blocare asupra tabelului.

  4. Partajat la nivel de tabel şi exclusiv la nivel de linie (Share Row Exclusive - SRX) se realizează prin:
    LOCK TABLE tabel IN SHARE ROW EXCLUSIVE MODE;
    

    O singură tranzacţie poate obţine acest mod de blocare asupra unui tabel. Ea poate face modificări, iar alte tranzacţii pot numai citi date. Pe lângă citire, o altă tranzacţie poate bloca partajat înregistrări dar fără să modifice date.

  5. Exclusiv la nivel de tabel (Exclusive - X): este cel mai restrictiv tip de blocare şi se poate obţine prin instrucţiunea:
    LOCK TABLE tabel IN EXCLUSIVE MODE;
    

    O altă tranzacţie poate numai interoga date.

Din cele descrise mai sus rezultă compatibilitătile (marcate cu "+") dintre două tipuri de blocări sau instrucţiuni SQL:
Tip blocare NotatieRS    RX    S      SRX  X      
Row Share RS + + ++ -
Row Exclusive RX + + -- -
Share S + - +- -
Share Row ExclusiveSRX + - -- -
Exclusive X - - -- -
SELECT   + + ++ +
INSERT, UPDATE, DELETERX + + -- -
SELECT... FOR UPDATE  + + -+ -

Observaţie. Orice tip de blocare permite consultarea datelor de către altă tranzacţie.
Instrucţiunile de modificare a dicţionarului bazei de date nu solicită blocări explicite, dar pentru execuţie au nevoie de o blocare exclusivă. Pentru execuţie se aşteaptă finalizarea tranzacţilor care au blocat componentele asupra cărora acţionează.

Viewuri utile

Tranzacţii autonome

O "tranzacţie autonomă" este o tranzacţie ce apare într-o procedură sau bloc anonim şi este total independentă de alte tranzacţii.

O tranzacţie curentă T este suspendată în momentul în care se apelează procedura/blocul anonim care conţine o tranzacţie autonomă TA.

...
- început tranzacţie T
- operaţii de modificare a datelor
- apel procedură/bloc
 
  procedura/blocul solicitat pentru execuţie:
- se defineşte o tranzacţie anonimă TA
- operaţii de modificare a datelor
- precizează modul de terminare a tranzacţiei anonime TA
- salvare/anulare modificări, fără să se ia
în considerare operaţiile de modificare din TA
 

O tranzacţie anonimă trebuie să fie declarată într-un bloc PL/SQL (bloc anonim, funcţie, procedură, metoda unui obiect). In partea de declaraţii trebuie să se adauge instrucţiunea (directivă de compilare):

PRAGMA AUTONOMOUS_TRANSACTION;

Exemplu de procedură:

PROCEDURE nume_procedura IS
  PRAGMA AUTONOMOUS_TRANSACTION;
	...
BEGIN
   modificari: insert/update/delete
   commit;
END;

Observaţie. O tranzacţie autonomă nu poate consulta tabele modificate în tranzacţia părinte şi pentru care nu s-a precizat modul de terminare (commit/rollback).

Exemplu pentru a vedea diferenţa dintre o tranzacţie autonomă şi un obişnuită.

CREATE TABLE test_t(a NUMBER(2));

INSERT INTO test_t (a) VALUES (1);
INSERT INTO test_t (a) VALUES (2);

SELECT * FROM test_t;

Ultima instrucţiune SELECT furnizează două înregistrări.
Vom folosi (pentru execuţie) o tranzacţie autonomă, definită într-un bloc anonim (poate fi memorată într-o procedură):

DECLARE
  PRAGMA AUTONOMOUS_TRANSACTION;
  i number;
BEGIN
  FOR i IN 3 .. 10 LOOP
    INSERT INTO test_t (a) VALUES (i);
  END LOOP;
  COMMIT;
END;

Acest bloc se va executa. Pentru că blocul s-a terminat de executat fără erori, înregistrările adăugate aici se vor păstra (se execută commit la final). Cu instrucţiunea:

SELECT * FROM test_t;

se vor obţine 10 înregistrări.
Dacă se execută instrucţiunile:

ROLLBACK;
SELECT * FROM test_t;

atunci se vor obţine numai 8 înregistrări, deoarece cele adăugate în tranzacţia curentă sunt eliminate.
Dacă se execută din nou aceleaşi instrucţiuni, fără să se utilizeze tranzacţia autonomă:

drop table test_t;
CREATE TABLE test_t(a NUMBER(2));

INSERT INTO test_t (a) VALUES (1);
INSERT INTO test_t (a) VALUES (2);

SELECT * FROM test_t;
DECLARE
  i number;
BEGIN
  FOR i IN 3 .. 10 LOOP
    INSERT INTO test_t (a) VALUES (i);
  END LOOP;
  COMMIT;
END;
/
ROLLBACK;
SELECT * FROM test_t;

atunci în final vor fi 10 înregistrări în tabel (modificările au fost salvate în blocul PL/SQL).

Tranzacţiile autonome se recomandă a fi folosite pentru execuţia unor operaţii care nu se fac în alte tranzacţii şi care trebuie efectuate indiferent de modul de terminare a tranzacţiilor. Se poate utiliza, de exemplu, pentru urmărirea operaţiilor din diverse tranzacţii.

Vom crea un tabel pentru inventarierea erorilor din anumite tranzacţii:

CREATE TABLE erori(
  utilizator varchar2(20),
  data date,
  mesaj VARCHAR2(2000)
);

Pentru introducerea de înregistrări în acest tabel vom folosi procedura:

CREATE OR REPLACE PROCEDURE adauga_eroare(utilizator IN VARCHAR2, mesaj IN VARCHAR2) AS
  PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
  INSERT INTO erori (utilizator, data, mesaj) VALUES (utilizator, SYSDATE, mesaj);
  COMMIT;
END;

Vom folosi tabelul anterior:

CREATE TABLE test_t(a NUMBER(2));

Vom adăuga înregistrări în tabelul test_t folosind următorul bloc:

BEGIN
  INSERT INTO test_t values (1);
  INSERT INTO test_t values (11);
  INSERT INTO test_t values (111);
  COMMIT;
EXCEPTION
  WHEN OTHERS THEN
    adauga_eroare(user, SQLERRM);
    ROLLBACK;
END;

Deoarece în acest bloc apare o eroare (tratată în partea EXCEPTION), toate modificările sunt anulate (în EXCEPTION apare ROLLBACK), dar în tabelul erori apare o înregistrare.

Utilizare tranzacţii în PHP

Completăm funcţiile descrise în utilizare OCI cu următoarele: Exemple: