Showing posts with label collection. Show all posts
Showing posts with label collection. Show all posts

Tuesday, May 18, 2010

Collection_Array

PL/SQL has three collection types, Tom often demos with Nested Table and Associative Array.
Associative Array is most flexible on element indexing. Nested Table is good enough for Bulk Fetching.

But Bryn Llewellyn, Oracle PL/SQL Product Manager likes to use VArray in his demo.
"the collection is best declared as a varray with a maximum size equal to Batchsize."
See http://www.oracle.com/technology/tech/pl_sql/pdf/doing_sql_from_plsql.pdf
Probably he knows that VARRAY implemented with the most efficient internal storage, and delivery the best performance, when correctly used; it may meet most of the requirements for loop batch bulk fetching.
To find out the details, you may do a simple RunStats benchmark.

  • Associative Array type(or index-by table)
TYPE population IS TABLE OF NUMBER INDEX BY VARCHAR2(64);

  • Nested Tables
TYPE nested_type IS TABLE OF VARCHAR2(30);

    • invoke EXTEND method to add elements later

  • Collection of ADT = UDT, Abstract datatype, User defined datatype:
CREATE OR REPLACE TYPE INVDB.NUMBER_TAB_TYPE is table of number;
/

select ... from TABLE(ADT_Table_Instance);

Comments, Bulk fetch into ADT is not efficient, you may see the workaround in paper doing_sql_from_plsql.pdf

  • Variable-size array (varray)
-- Code_30 Many_Row_Select.sql
Batchsize constant pls_integer := 1000;

type Result_t is record(PK t.PK%type, v1 t.v1%type);
type Results_t is varray(1000) of Result_t;
Results Results_t;

Concept

  • Associative Array: sparse array.
  • Nested Table or ADT/UDT : dense array
REM Associative Array
declare
   type date_aat is table of date index by binary_integer;
   l_data date_aat;
begin
   l_data(-200) := sysdate;
   l_data(+200) := sysdate+1;
end;
/

collection(elements), e.g. collection_instance(element_unique_subscript_index_number)
record_name.field_name

Tuesday, January 06, 2009

3 PL/SQL approaches fetch data into ADT collection

Goal
----
There are 3 approaches to fetch data into schema object collection type.
Benchmark to find the best one.

Solution
--------
Setup test case, benchmark with RunStats and SQL trace.

Result
------
PL/SQL native collection(Associative Array and Nested Table) is the fastest way to get data from SQL.
But some time you need to query against a Collection/Array, you have to declare it as an schema object type.

To fetch data into schema object type collection, here list 3 approaches Pros and Cons:

1) Convert multi columns to object type, bulk collect into collection
Pros: code is simple.
Cons: 50% more CPU time compare to approach 2; about same amount of latches.

2) Use multiset, constructs the required collection of ADTs directly in SQL as a select list element in a single row
Pros: Faster, 50% better than approach 1 in my test case.
Cons: code is a little complex, not support SQL Returning clause in 10.1.

3) bulk collect into PL/SQL native collection, copy data into object type collection
Pros: Fastest, 67% better than approach 1 in my test case, 50% less latches.
Cons: use 100% more PGA memory, code is a little complex and not easy to maintain.

Note: ADT: Abstract Data Type, also calls User Define Datatype

Setup
-----

drop TYPE abelisting.book_chg_tab;
drop TYPE abelisting.book_chg_rec;

create or replace TYPE abelisting.book_chg_rec AS OBJECT
(
listingsid number(20),
qty number(5),
delete_flag number(1),
update_date date
);
/

create or replace TYPE abelisting.book_chg_tab IS TABLE OF abelisting.book_chg_rec;
/

DROP TABLE abelisting.book_delete_stage_part;

CREATE TABLE abelisting.book_delete_stage_part (
listingsid number(20),
qty number(5),
delete_flag number(1),
update_date timestamp(3) default SysTimestamp,
part_mi number(2) default mod(to_number(to_char(sysdate,'MI')),10)
)
PARTITION BY LIST (part_mi) (
PARTITION data_part_0 VALUES (0),
PARTITION data_part_1 VALUES (1),
PARTITION data_part_2 VALUES (2),
PARTITION data_part_3 VALUES (3),
PARTITION data_part_4 VALUES (4),
PARTITION data_part_5 VALUES (5),
PARTITION data_part_6 VALUES (6),
PARTITION data_part_7 VALUES (7),
PARTITION data_part_8 VALUES (8),
PARTITION data_part_9 VALUES (9),
PARTITION dummy_null VALUES (NULL),
PARTITION dummy_other VALUES (DEFAULT)
)
tablespace data_auto
nologging;

insert into /*+ append */ abelisting.book_delete_stage_part(listingsid,delete_flag,update_date, part_mi)
select trunc(dbms_random.value(1,20000)), mod(level,2), trunc(sysdate,'DD') + mod(level,60)/1440,
mod(level,10)
from dual
connect by level <= 30000;
commit;

exec dbms_stats.gather_table_stats(user,'BOOK_DELETE_STAGE_PART');

Code
----

*) RunStats

# multiset vs. bulk collect into object_array

declare
lt_chg abelisting.book_chg_tab;
-- type my_book_chg_tab is table of abelisting.book_delete_stage%ROWTYPE;
-- lt_chg my_book_chg_tab;
type tab_chg_array is table of abelisting.book_delete_stage_part%rowtype;
lt_chg_array tab_chg_array;
begin
runStats_pkg.rs_start;
select cast(multiset(
select book_chg_rec(LISTINGSID, QTY, DELETE_FLAG, UPDATE_DATE) from abelisting.book_delete_stage_part
) as book_chg_tab)
into lt_chg
from dual;
end;
/

declare
lt_chg abelisting.book_chg_tab;
type item_chg_array is table of abelisting.book_delete_stage_part%rowtype;
lt_chg_array item_chg_array;
begin
runStats_pkg.rs_middle;
select book_chg_rec(LISTINGSID, QTY, DELETE_FLAG, UPDATE_DATE)
bulk collect into lt_chg
from abelisting.book_delete_stage_part
;
runStats_pkg.rs_stop(5);
end;
/

Run1 ran in 24 hsecs
Run2 ran in 32 hsecs
run 1 ran in 75% of the time

Name Run1 Run2 Diff
STAT...recursive calls 11 4 -7
LATCH.active service list 0 7 7
LATCH.cache buffers chains 909 902 -7
LATCH.session idle bit 8 0 -8
STAT...Elapsed Time 26 35 9
STAT...recursive cpu usage 23 34 11
STAT...db block gets 63 51 -12
STAT...db block gets from cach 63 51 -12
STAT...session logical reads 390 377 -13
STAT...workarea memory allocat -34 -9 25
STAT...CPU used when call star 28 0 -28
STAT...CPU used by this sessio 28 0 -28
STAT...DB time 28 0 -28
LATCH.library cache lock 41 12 -29
LATCH.JS queue state obj latch 0 30 30
LATCH.library cache pin 64 24 -40
LATCH.row cache objects 69 27 -42
LATCH.shared pool 58 15 -43
LATCH.enqueues 8 68 60
LATCH.enqueue hash chains 6 69 63
LATCH.library cache 130 36 -94
STAT...undo change vector size 2,808 2,908 100
STAT...redo size 3,956 4,064 108
LATCH.simulator hash latch 2 131 129
STAT...bytes sent via SQL*Net 218 0 -218
STAT...bytes received via SQL* 760 0 -760
STAT...buffer is pinned count 29,796 0 -29,796
STAT...session uga memory max 196,500 0 -196,500
STAT...session pga memory max 6,815,744 393,216 -6,422,528
STAT...session pga memory 0 7,208,960 7,208,960

Run1 latches total versus runs -- difference and pct
Run1 Run2 Diff Pct
1,337 1,356 19 98.60%

# multiset vs. plsql_collection copy to object array

declare
lt_chg abelisting.book_chg_tab;
type tab_chg_array is table of abelisting.book_delete_stage_part%rowtype;
lt_chg_array tab_chg_array;
begin
runStats_pkg.rs_start;
select cast(multiset(
select book_chg_rec(LISTINGSID, QTY, DELETE_FLAG, UPDATE_DATE) from abelisting.book_delete_stage_part
) as book_chg_tab)
into lt_chg
from dual;
end;
/

declare
lt_chg abelisting.book_chg_tab;
type item_chg_array is table of abelisting.book_delete_stage_part%rowtype;
lt_chg_array item_chg_array;
begin
runStats_pkg.rs_middle;

select LISTINGSID, QTY, DELETE_FLAG, UPDATE_DATE, Null
bulk collect into lt_chg_array
from abelisting.book_delete_stage_part;

lt_chg := abelisting.book_chg_tab();
lt_chg.extend(lt_chg_array.count());
For i in lt_chg_array.First() .. lt_chg_array.Last()
Loop
lt_chg(i) := book_chg_rec(lt_chg_array(i).LISTINGSID,
lt_chg_array(i).QTY ,
lt_chg_array(i).DELETE_FLAG,
lt_chg_array(i).UPDATE_DATE
);
End Loop;
runStats_pkg.rs_stop(5);
end;
/

Run1 ran in 27 hsecs
Run2 ran in 17 hsecs
run 1 ran in 158.82% of the time

Name Run1 Run2 Diff
STAT...opened cursors cumulati 8 2 -6
LATCH.active service list 0 7 7
LATCH.child cursor hash table 7 0 -7
STAT...recursive calls 12 4 -8
LATCH.session idle bit 8 0 -8
STAT...Elapsed Time 29 20 -9
STAT...db block gets 63 53 -10
STAT...db block gets from cach 63 53 -10
STAT...recursive cpu usage 23 12 -11
STAT...workarea memory allocat 12 -7 -19
STAT...calls to get snapshot s 62 38 -24
STAT...table fetch by rowid 24 0 -24
STAT...index fetch by key 24 0 -24
LATCH.checkpoint queue latch 8 32 24
STAT...rows fetched via callba 24 0 -24
STAT...CPU used when call star 28 0 -28
STAT...DB time 28 0 -28
STAT...CPU used by this sessio 28 0 -28
LATCH.JS queue state obj latch 0 30 30
STAT...free buffer inspected 0 38 38
STAT...hot buffers moved to he 0 48 48
STAT...buffer is not pinned co 48 0 -48
LATCH.enqueues 10 70 60
LATCH.enqueue hash chains 8 71 63
LATCH.SQL memory manager worka 4 71 67
STAT...consistent gets 397 327 -70
LATCH.cache buffers chains 973 903 -70
STAT...consistent gets from ca 397 327 -70
STAT...consistent gets - exami 72 1 -71
STAT...session logical reads 460 380 -80
STAT...undo change vector size 2,804 2,904 100
LATCH.simulator hash latch 130 3 -127
STAT...redo size 3,952 4,120 168
LATCH.row cache objects 193 18 -175
STAT...bytes sent via SQL*Net 218 0 -218
LATCH.library cache lock 234 12 -222
LATCH.library cache pin 256 25 -231
LATCH.shared pool 258 13 -245
LATCH.library cache 559 39 -520
STAT...bytes received via SQL* 1,067 0 -1,067
STAT...buffer is pinned count 29,796 0 -29,796
STAT...session uga memory 65,464 0 -65,464
STAT...session uga memory max 193,032 68,932 -124,100
STAT...session pga memory max 6,815,744 9,895,936 3,080,192
STAT...session pga memory 65,536 16,646,144 16,580,608

Run1 latches total versus runs -- difference and pct
Run1 Run2 Diff Pct
2,691 1,330 -1,361 202.33%

*) SQL Trace

ALTER SESSION SET TRACEFILE_IDENTIFIER="yi";
EXECUTE DBMS_MONITOR.SESSION_TRACE_ENABLE(waits => TRUE, binds => True);

declare
lt_chg abelisting.book_chg_tab;
begin
select cast(multiset(
select book_chg_rec(LISTINGSID, QTY, DELETE_FLAG, UPDATE_DATE) from abelisting.book_delete_stage_part
) as book_chg_tab)
into lt_chg
from dual;
end;
/

declare
lt_chg abelisting.book_chg_tab;
begin
select book_chg_rec(LISTINGSID, QTY, DELETE_FLAG, UPDATE_DATE)
bulk collect into lt_chg
from abelisting.book_delete_stage_part
;
end;
/

EXECUTE DBMS_MONITOR.SESSION_TRACE_DISABLE();


tkprof report
-------------
SELECT book_chg_rec(LISTINGSID, QTY, DELETE_FLAG, UPDATE_DATE)
FROM
ABELISTING.BOOK_DELETE_STAGE_PART

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 1 0.34 0.33 0 316 0 30000
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 3 0.34 0.33 0 316 0 30000

SELECT CAST(MULTISET( SELECT book_chg_rec(LISTINGSID, QTY, DELETE_FLAG,
UPDATE_DATE)
FROM
ABELISTING.BOOK_DELETE_STAGE_PART ) AS BOOK_CHG_TAB) FROM DUAL

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 1 0.21 0.22 0 316 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 3 0.21 0.22 0 316 0 1

Misses in library cache during parse: 0
Optimizer mode: FIRST_ROWS
Parsing user id: 35 (recursive depth: 1)

Rows Row Source Operation
------- ---------------------------------------------------
1 FAST DUAL (cr=0 pr=0 pw=0 time=9 us)

SELECT LISTINGSID, QTY, DELETE_FLAG, UPDATE_DATE, NULL
FROM
ABELISTING.BOOK_DELETE_STAGE_PART

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 1 0.11 0.10 0 316 0 30000
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 3 0.11 0.10 0 316 0 30000

Misses in library cache during parse: 1
Optimizer mode: FIRST_ROWS
Parsing user id: 35 (recursive depth: 1)

Rows Row Source Operation
------- ---------------------------------------------------
30000 PARTITION LIST ALL PARTITION: 1 12 (cr=316 pr=0 pw=0 time=90091 us)
30000 TABLE ACCESS FULL BOOK_DELETE_STAGE_PART PARTITION: 1 12 (cr=316 pr=0 pw=0 time=30379 us)
.