Problēma:
Lielāka datu apjoma labošana iekš PL/SQL
vietās kur vienkārši SQL
rakstīt būtu nepatīkami/neefektīvi.
Risinājums #1:
izmantot FOR
ciklu lai apstaigātu datu kursoru un ar UPDATE
veikt labojumi ierakstam.
Risinājums #2:
izmantot oracle kolekcijas
- datus ielādēt kolekcijā izmantojot
BULK COLLECT
(ja atmiņa pietiks tad dati glabāsies iekš RAM)
SELECT * BULK COLLECT INTO v_bench_data_table FROM bench_data_table;
- ciklā apstaigāt kolekciju (situācijas kurās vajag, piemēram, apskatīt vēlreiz iepriekšējo ierakstu vai kas sarežģītāks, kas
SQL
būtu nesmuki)
FOR i IN v_bench_data_table.first .. v_bench_data_table.last LOOP
v_bench_data_table(i).x := dbms_random.string( 'a', TRUNC( dbms_random.value( 5, 30 ) ) ); -- tikai piemērs, te būtu jābūt daudz sarežģītākam darbam.
END LOOP;
- saglabāt rezultātu no kolekcijas uz datu tabulu izmantojot
FORALL
FORALL i IN v_bench_data_table.first .. v_bench_data_table.last
UPDATE bench_data_table set x = v_bench_data_table(i).x WHERE x = v_bench_data_table(i).x;
Eksperiments:
CREATE GLOBAL TEMPORARY TABLE bench_data_table ( x VARCHAR2(50) );
INSERT INTO bench_data_table
SELECT dbms_random.string( 'a', TRUNC( dbms_random.value( 5, 30 ) ) ) x
FROM dual
CONNECT BY level <= 10000;
DECLARE
t TIMESTAMP;
TYPE t_rows IS TABLE OF bench_data_table%rowtype;
v_bench_data_table t_rows := t_rows();
BEGIN
t := systimestamp;
-- ielasam visus datus kolekcijā
SELECT * BULK COLLECT INTO v_bench_data_table FROM bench_data_table;
-- apstradājam katru kolekcijas ierakstu
FOR i IN v_bench_data_table.first .. v_bench_data_table.last LOOP
v_bench_data_table(i).x := dbms_random.string( 'a', TRUNC( dbms_random.value( 5, 30 ) ) ); -- tikai piemērs, te būtu jābūt daudz sarežģītākam darbam.
END LOOP;
-- saglabājam rezultātu
FORALL i IN v_bench_data_table.first .. v_bench_data_table.last
update bench_data_table set x = v_bench_data_table(i).x WHERE x = v_bench_data_table(i).x;
dbms_output.put_line('BULK COLLECT + FORALL ' || extract (second from systimestamp - t) * 1000 || '(ms)');
-- tas pasts tikai izmantojot update iekš cikla
t := systimestamp;
FOR i IN (SELECT x FROM bench_data_table) LOOP
UPDATE bench_data_table SET x = dbms_random.string( 'a', TRUNC( dbms_random.value( 5, 30 ) ) ) WHERE x = i.x;
END LOOP;
dbms_output.put_line('UPDATE + FOR LOOP ' || extract (second from systimestamp - t) * 1000 || '(ms)');
END;
/
DROP table bench_data_table;
Skripts salīdzina abus rezultātus un izvada patērēto laiku abiem risinājumiem un izvada uz DBMS_OUTPUT.
Risinājums#2 strādā par ~24% ātrāk nekā Risinājums#1.
Rezultāta piemērs:
BULK COLLECT + FORALL 3220,063(ms)
UPDATE + FOR LOOP 4206,575(ms)
Diemžēl Oracle kolekcijas nav tā sintaktiski patīkamākā fīča, bet ātrdarbības uzlabojums kritiskās vietās var noderēt. Var arī rasties problēmas, ja apstrādājamo datu apjoms ir lielāks nekā RAMā lien.