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):
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.
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/fenomene | citirile inconsistente | citirile nerepetitive | citirile fantomă |
Citiri nesalvate | + | + | + |
Citiri salvate | - | + | + |
Citiri repetabile | - | - | + |
Seriabilitate | - | - | - |
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.
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.
create table stud(nume varchar2(30), prenume varchar2(30), anadmitere char(4));
Exemplul 1:
insert into stud select nume,prenume,anadmitere from studenti where substr(anadmitere,3,2)='99'; 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,anadmitere from studenti where substr(anadmitere,3,2)='99'; 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 T1 | Tranzacţia T2 |
delete from stud; insert into stud select nume,prenume,anadmitere from studenti where substr(anadmitere,3,2)='99'; commit; select * from stud where anadmitere='1999' and nume='Pop' and prenume='Ion'; /* se obtin datele pentru 's1' */ |
|
update stud set nume='Popescu' where anadmitere='1999' and nume='Pop' and prenume='Ion'; insert into stud(nume,prenume,anadmitere) values('Pop','Ion','2000'); commit; /* se actualizeaza datele pentru 's1' la 's2', se adauga 's3' */ |
|
select * from stud where anadmitere='1999' and nume='Popescu' and prenume='Ion''; /* 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 anadmitere='1999' and nume='Pop' and prenume='Ion'; /* 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 anadmitere in ('1998','2001'); insert into stud select nume,prenume,anadmitere from studenti where substr(anadmitere,3,2)='98'; 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 T1 | Tranzacţia T2 |
delete from stud; INSERT INTO stud(anadmitere,nume,prenume) VALUES ('2001','Aa','Bb'); INSERT INTO stud(anadmitere,nume,prenume) VALUES ('2002','Xx','Yy'); commit; update stud set prenume='Cc' where anadmitere='2001'; select * from stud;--rezultat: NUME PRENUME ANADMITERE -------------------- -------------------- ------------- Aa Cc 2001 Xx Yy 2002 |
|
update stud set prenume='Zz' where anadmitere='2002'; select * from stud;--rezultat: NUME PRENUME ANADMITERE -------------------- -------------------- ------------- Aa Bb 2001 Xx Zz 2002 |
|
update stud set prenume='Uu' where anadmitere='2002'; -- se asteapta deblocarea inregistrarii |
|
update stud set prenume='Vv' where anadmitere='2001'; -- 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 ANADMITERE -------------------- -------------------- ------------- Aa Vv 2001 Xx Zz 2002 |
|
commit; select * from stud;--rezultat: NUME PRENUME ANADMITERE -------------------- -------------------- ------------- Aa Bb 2001 Xx Yy 2002 |
|
commit; |
|
select * from stud;--rezultat: NUME PRENUME ANADMITERE -------------------- -------------------- ------------- Aa Vv 2001 Xx Zz 2002 |
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.
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:
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;
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 T1 | Tranzacţia T2 |
-- se foloseste tabelul -- create table stud(nume varchar2(30), prenume varchar2(30), anadmitere char(4)); delete from stud; INSERT INTO stud(anadmitere,nume,prenume) VALUES ('2001','Aa','Bb'); INSERT INTO stud(anadmitere,nume,prenume) VALUES ('2002','Xx','Yy'); commit; select * from stud where anadmitere in ('2001','2002') for update nowait; -- se realizeaza blocarea celor doua inregistrari-- rezultat NUME PRENUME ANADMITERE -------------------- -------------------- ------------- Aa Bb 2001 Xx Yy 2002 |
|
select * from stud; -- inregistrarile blocate de T1 se pot citi-- rezultat NUME PRENUME ANADMITERE -------------------- -------------------- ------------- Aa Bb 2001 Xx Yy 2002 update stud set prenume='Dd' where anadmitere='2001'; -- actualizarea este in asteptare, inregistrarea este inca blocata |
|
update stud set prenume='Cc' where anadmitere='2001'; -- actualizarea din T1 se face commit; |
|
-- actualizarea din T2 se face -- tranzactia nu e terminata, o inreg. e blocata |
|
select * from stud where anadmitere in ('2001','2002') 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 |
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;
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.
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.
LOCK TABLE tabel IN EXCLUSIVE MODE;
O altă tranzacţie poate numai interoga date.
Tip blocare | Notatie | RS | RX | S | SRX | X |
Row Share | RS | + | + | + | + | - |
Row Exclusive | RX | + | + | - | - | - |
Share | S | + | - | + | - | - |
Share Row Exclusive | SRX | + | - | - | - | - |
Exclusive | X | - | - | - | - | - |
SELECT | + | + | + | + | + | |
INSERT, UPDATE, DELETE | RX | + | + | - | - | - |
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ă.
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.
bool oci_execute(resource $id_instructiune [, int parametru])care execută o instrucţiune (identificată de primul argument) pregătită de oci_parse, iar parametru precizează modul în care sunt tratate modificările din baza de date (dacă se execută astfel de instrucţiuni). Valorile posibile pentru acest parametru sunt:
bool oci_commit(resource $id_instructiune)care va executa o instrucţiune COMMIT. Valoarea furnizată de funcţie este TRUE în cazul succesului sau FALSE în cazul în care salvarea modificărilor nu s-a putut realiza.
bool oci_rollback(resource $id_instructiune)care va executa o instrucţiune ROLLBACK, deci modificările anterioare din tranzacţia curentă sunt anulate. Valoarea furnizată de funcţie este TRUE sau FALSE (operaţia s-a put sau nu s-a putut executa).
<?php function executa($con, $query) { $s = oci_parse($con, $query); oci_execute($s, OCI_DEFAULT); oci_fetch_all($s, $r); //toate valorile se obtin intr-o matrice echo "<pre>"; var_dump($r); //extrage matricea echo "</pre>"; } $con1 = oci_connect("master", "master", "orcl10"); //prima conectiune $x = oci_parse($con1, "insert into test_t values (51)"); $r = oci_execute($x, OCI_DEFAULT); // implicit nu apare COMMIT executa($con1, "select * from test_t"); $con2 = oci_new_connect("master", "master", "orcl10"); //a doua conectiune executa($con2, "select * from test_t"); //$con2 nu vede inregistrarea inserata anterior de $con1 $x = oci_parse($con2, "insert into test_t values (61)"); $r = oci_execute($x, OCI_DEFAULT); // implicit nu apare COMMIT executa($con1, "select * from test_t"); //$con1 vede numai inregistrarea introdusa de ea oci_commit($con2); executa($con1, "select * from test_t"); //$con1 vede toate inregistrarile oci_close($con1); oci_close($con2); //inregistrarea adaugata de $con1 se pierde (nu exista COMMIT) //inregistrarea adaugata de $con2 se pastreaza ?>
<?php //presupunem creat tabelul: //create table test_t1 (id number, text varchar2(100)); //comparatie intre doua tipuri de salvare a modificarilor function executa1($con, $query) { $s = oci_parse($con, $query); oci_execute($s, OCI_DEFAULT); //nu se executa commit automat } function executa2($con, $query) { $s = oci_parse($con, $query); oci_execute($s); //commit automat } function nr_inreg($con){ $idi = oci_parse($con, "select count(*) nr from test_t1"); oci_execute($idi); $r=oci_fetch_row($idi); echo "Nr.de inreg.: " . $r[0] . "<br>"; } $con = oci_connect("leon", "leon", "orcl"); executa2($con, "delete from test_t1"); $timp1 = microtime(TRUE); echo $timp1."<br>"; for ($i=1; $i <=1000; $i++){ executa1($con, "insert into test_t1 values(".$i.", to_char(sysdate,'dd.mm.yyyy hh:mi:ss'))"); } $timp2 = microtime(TRUE); nr_inreg($con); echo "Timpul de executie dupa adaugare: ".round($timp2-$timp1,3) . " secunde<br>"; oci_commit($con); $timp2 = microtime(TRUE); //nr_inreg($con); echo "Timpul de executie dupa adaugare si commit la final: <b>".round($timp2-$timp1,3) . " secunde</b><br>"; executa2($con, "delete from test_t1"); $timp3 = microtime(TRUE); for ($i=1; $i<=1000; $i++){ executa2($con, "insert into test_t1 values(".$i.", to_char(sysdate,'dd.mm.yyyy hh:mi:ss'))"); } $timp4 = microtime(TRUE); nr_inreg($con); echo "Timpul de executie dupa adaugare si commit la fiecare adaugare: <b>".round($timp4-$timp3,3) . " secunde</b>"; executa2($con, "delete from test_t1"); oci_close($con); ?>