Monday, October 25, 2010

Nested Tables Vs VArrays

  • Nested tables may be modified (inserted/updated/deleted) from, using SQL.
rajesh@10GR2> create type person_add as object(
  2     street_address1 varchar2(40),
  3     street_address2 varchar2(40),
  4     city varchar2(40),
  5     state varchar2(40),
  6     zip number
  7  );
  8  /

Type created.

Elapsed: 00:00:00.09
rajesh@10GR2>
rajesh@10GR2> create type person_typ is table of person_add;
  2  /

Type created.

Elapsed: 00:00:00.12
rajesh@10GR2>
rajesh@10GR2> create table person(
  2     person_id number,
  3     first_name varchar2(30),
  4     last_name  varchar2(30),
  5     address   person_typ
  6  ) nested table address store as address_ntt;

Table created.

Elapsed: 00:00:00.17
rajesh@10GR2>
rajesh@10GR2> insert into person values (1,'Test1','Test1',person_typ(
  2                     person_add('123 main1',null,'CA','MN',123),
  3                     person_add('245 main1',null,'NJ','MN',245))
  4                                               );

1 row created.

Elapsed: 00:00:00.01
rajesh@10GR2> commit;

Commit complete.

Elapsed: 00:00:00.01
rajesh@10GR2> select p_add.street_address1,p_add.street_address2
  2  from person p,table(p.address) p_add;

STREET_ADDRESS1                          STREET_ADDRESS2
---------------------------------------- ----------------------------------------
123 main1
245 main1

Elapsed: 00:00:00.04
rajesh@10GR2>
rajesh@10GR2> update table (select address from person)
  2  set street_address2 = street_address1;

2 rows updated.

Elapsed: 00:00:00.01
rajesh@10GR2>
rajesh@10GR2> select p_add.street_address1,p_add.street_address2
  2  from person p,table(p.address) p_add;

STREET_ADDRESS1                          STREET_ADDRESS2
---------------------------------------- ----------------------------------------
123 main1                                123 main1
245 main1                                245 main1

Elapsed: 00:00:00.03
rajesh@10GR2>
rajesh@10GR2> delete from table (select address from person);

2 rows deleted.

Elapsed: 00:00:00.01
rajesh@10GR2>
rajesh@10GR2> select p_add.street_address1,p_add.street_address2
  2  from person p,table(p.address) p_add;

no rows selected

Elapsed: 00:00:00.00
rajesh@10GR2>
rajesh@10GR2>

  • VARRAYs must be procedurally modified. You cannot: UPDATE , DELETE as you could with a nested table
rajesh@10GR2> create type person_add as object(
  2     street_address1 varchar2(40),
  3     street_address2 varchar2(40),
  4     city varchar2(40),
  5     state varchar2(40),
  6     zip number
  7  );
  8  /

Type created.

Elapsed: 00:00:00.09
rajesh@10GR2>
rajesh@10GR2> create type person_typ is Varray(3) of person_add;
  2  /

Type created.

Elapsed: 00:00:00.04
rajesh@10GR2> create table person(
  2     person_id number,
  3     first_name varchar2(30),
  4     last_name  varchar2(30),
  5     address   person_typ
  6  );

Table created.

Elapsed: 00:00:00.04
rajesh@10GR2>
rajesh@10GR2> insert into person values (1,'Test1','Test1',person_typ(
  2                     person_add('123 main1',null,'CA','MN',123),
  3                     person_add('245 main1',null,'NJ','MN',245))
  4                                               );

1 row created.

Elapsed: 00:00:00.01
rajesh@10GR2> commit;

Commit complete.

Elapsed: 00:00:00.03
rajesh@10GR2> select p_add.street_address1,p_add.street_address2
  2  from person p,table(p.address) p_add;

STREET_ADDRESS1                          STREET_ADDRESS2
---------------------------------------- ----------------------------------------
123 main1
245 main1

Elapsed: 00:00:00.01
rajesh@10GR2>
rajesh@10GR2> update table (select address from person)
  2  set street_address2 = street_address1;
set street_address2 = street_address1
    *
ERROR at line 2:
ORA-25015: cannot perform DML on this nested table view column


Elapsed: 00:00:00.00
rajesh@10GR2> delete from table (select address from person);
delete from table (select address from person)
                                       *
ERROR at line 1:
ORA-22906: cannot perform DML on expression or on nested table view column

Elapsed: 00:00:00.01
rajesh@10GR2>

Piece-wise updates of a varray value are not supported. Thus, when a varray is updated, the entire old collection is replaced by the new collection

  • Nested tables will perform a relational JOIN to bring the collect back with the row. For small collections, this may be expensive
rajesh@10GR2> create type person_add as object(
  2     street_address1 varchar2(40),
  3     street_address2 varchar2(40),
  4     city varchar2(40),
  5     state varchar2(40),
  6     zip number
  7  );
  8  /

Type created.

Elapsed: 00:00:00.10
rajesh@10GR2> create type person_typ_ntt is table of person_add;
  2  /

Type created.

Elapsed: 00:00:00.06
rajesh@10GR2>
rajesh@10GR2> create table person_ntt(
  2     person_id number,
  3     first_name varchar2(30),
  4     last_name  varchar2(30),
  5     address   person_typ_ntt
  6  )nested table address store as person_ntt_nest;

Table created.

Elapsed: 00:00:00.09
rajesh@10GR2>
rajesh@10GR2> insert into person_ntt
  2  select object_id,object_name,object_name,person_typ_ntt(
  3                     person_add('123 main1',null,'CA','MN',123),
  4                     person_add('245 main1',null,'NJ','MN',245))
  5  from all_objects;

56175 rows created.

Elapsed: 00:00:20.32
rajesh@10GR2> commit;

Commit complete.

Elapsed: 00:00:00.03
rajesh@10GR2> begin
  2     dbms_stats.gather_table_stats(ownname=>user,tabname=>'PERSON_NTT',estimate_percent=>100,method_opt=>'for all columns size 254');
  3  end;
  4  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:02.65

select p.person_id,p_add.street_address1,p_add.street_address2
from person_ntt p,table(p.address) p_add

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.01       0.31          0          1          0           0
Execute      1      0.00       0.02          0          0          0           0
Fetch      750      0.23       0.27          0       2007          0      112350
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total      752      0.25       0.60          0       2008          0      112350

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 100 

Rows     Row Source Operation
-------  ---------------------------------------------------
 112350  HASH JOIN  (cr=2007 pr=0 pw=0 time=486462 us)
  56175   TABLE ACCESS FULL PERSON_NTT (cr=581 pr=0 pw=0 time=71 us)
 112350   TABLE ACCESS FULL PERSON_NTT_NEST (cr=1426 pr=0 pw=0 time=112409 us)

  • VARRAYs do not join. The data is accessed inline for small collections, and as a LOB segment for large collections. In general, there will be less overhead associated with accessing a VARRAY compared to a nested table. There is potentially more overhead associated with updating a VARRAY as compared to a nested table however, since the entire VARRAY must be replaced ‐ not just an element of it.

rajesh@10GR2> create type person_varr is Varray(60000) of person_add;
  2  /

Type created.

Elapsed: 00:00:00.07
rajesh@10GR2> create table person_varr_tab(
  2     person_id number,
  3     first_name varchar2(30),
  4     last_name  varchar2(30),
  5     address   person_varr
  6  );

Table created.

Elapsed: 00:00:00.54
rajesh@10GR2>
rajesh@10GR2> insert into person_varr_tab
  2  select object_id,object_name,object_name,person_varr(
  3                     person_add('123 main1',null,'CA','MN',123),
  4                     person_add('245 main1',null,'NJ','MN',245))
  5  from all_objects;

56177 rows created.

Elapsed: 00:00:02.73
rajesh@10GR2> commit;

Commit complete.

Elapsed: 00:00:00.07
rajesh@10GR2> begin
  2     dbms_stats.gather_table_stats(ownname=>user,tabname=>'PERSON_VARR_TAB',estimate_percent=>100,method_opt=>'for all columns size 254');
  3  end;
  4  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:02.06

select p.person_id,p_add.street_address1,p_add.street_address2
from person_varr_tab p,table(p.address) p_add

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      751      0.95       0.79          0       1914          0      112354
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total      753      0.95       0.79          0       1914          0      112354

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 100 

Rows     Row Source Operation
-------  ---------------------------------------------------
 112354  NESTED LOOPS  (cr=1914 pr=0 pw=0 time=1011273 us)
  56177   TABLE ACCESS FULL PERSON_VARR_TAB (cr=1914 pr=0 pw=0 time=112428 us)
 112354   COLLECTION ITERATOR PICKLER FETCH (cr=0 pr=0 pw=0 time=275780 us)

  • Nested tables are physically stored as a parent child table with surrogate keys.
test_user@10GR2> create type person_add as object(
  2     street_address1 varchar2(40),
  3     street_address2 varchar2(40),
  4     city varchar2(40),
  5     state varchar2(40),
  6     zip number
  7  );
  8  /

Type created.

Elapsed: 00:00:00.10
test_user@10GR2> create type person_typ_ntt is table of person_add;
  2  /

Type created.

Elapsed: 00:00:00.06
test_user@10GR2> create table person_ntt(
  2     person_id number,
  3     first_name varchar2(30),
  4     last_name  varchar2(30),
  5     address   person_typ_ntt
  6  )nested table address store as person_ntt_nest;

Table created.

Elapsed: 00:00:00.15
test_user@10GR2>
test_user@10GR2>
test_user@10GR2> column obj# format 99999999;
test_user@10GR2> column col# format 99999;
test_user@10GR2> column name format a30;
test_user@10GR2>
test_user@10GR2> SELECT obj#, col#, name
  2  FROM sys.col$
  3  WHERE obj# IN
  4  (SELECT object_id
  5  FROM user_objects
  6  WHERE object_name IN ('PERSON_NTT','PERSON_NTT_NEST'))
  7  order by 1,2
  8  /

     OBJ#   COL# NAME
--------- ------ ------------------------------
   146579      1 PERSON_ID
   146579      2 FIRST_NAME
   146579      3 LAST_NAME
   146579      4 ADDRESS
   146579      4 SYS_NC0000400005$
   146580      0 NESTED_TABLE_ID
   146580      0 SYS_NC_ROWINFO$
   146580      1 STREET_ADDRESS1
   146580      2 STREET_ADDRESS2
   146580      3 CITY
   146580      4 STATE
   146580      5 ZIP

12 rows selected.

Elapsed: 00:00:00.03
test_user@10GR2>

VARRAYs are stored as a RAW column or as a LOB. There is minimal overhead introduced for the functionality.

test_user@10GR2> create type person_add as object(
  2     street_address1 varchar2(40),
  3     street_address2 varchar2(40),
  4     city varchar2(40),
  5     state varchar2(40),
  6     zip number
  7  );
  8  /

Type created.

Elapsed: 00:00:00.04
test_user@10GR2> create type person_varr is Varray(15) of person_add;
  2  /

Type created.

Elapsed: 00:00:00.04
test_user@10GR2> create table PERSON_VARR_TAB(
  2     person_id number,
  3     first_name varchar2(30),
  4     last_name  varchar2(30),
  5     address   person_varr
  6  );

Table created.

Elapsed: 00:00:00.04
test_user@10GR2>
test_user@10GR2> SELECT  col#, name,length
  2  FROM sys.col$
  3  WHERE obj# =
  4  (SELECT object_id
  5  FROM user_objects
  6  WHERE object_name = 'PERSON_VARR_TAB')
  7  order by 1,2;

      COL# NAME                               LENGTH
---------- ------------------------------ ----------
         1 PERSON_ID                              22
         2 FIRST_NAME                             30
         3 LAST_NAME                              30
         4 ADDRESS                              3002

Elapsed: 00:00:00.01
test_user@10GR2>
test_user@10GR2> Select object_type, object_name,
  2  decode(status,'INVALID','*','') status,
  3  tablespace_name
  4  from user_objects a, user_segments b
  5  where a.object_name = b.segment_name (+)
  6  order by object_type, object_name;

OBJECT_TYPE         OBJECT_NAME                    S TABLESPACE_NAME
------------------- ------------------------------ - ---------------------
TABLE               PERSON_VARR_TAB                  USERS
TYPE                PERSON_ADD
TYPE                PERSON_VARR

Elapsed: 00:00:00.12
test_user@10GR2>

Oracle has added a 3000 byte column to support our VARRAY implementation. The data for our VARRAY will be stored inline (in the row itself). This raises an interesting question; what will happen if our array could exceed 4,000 bytes ?

test_user@10GR2> alter table PERSON_VARR_TAB drop column address;

Table altered.

Elapsed: 00:00:00.15
test_user@10GR2> create or replace type person_varr is Varray(25) of person_add;
  2  /

Type created.

Elapsed: 00:00:00.14
test_user@10GR2> alter table person_varr_tab add address person_varr;

Table altered.

Elapsed: 00:00:00.06
test_user@10GR2>
test_user@10GR2> Select object_type, object_name,
  2  decode(status,'INVALID','*','') status,
  3  tablespace_name
  4  from user_objects a, user_segments b
  5  where a.object_name = b.segment_name (+)
  6  order by object_type, object_name;

OBJECT_TYPE         OBJECT_NAME                    S TABLESPACE_NAME
------------------- ------------------------------ - ------------------------------
LOB                 SYS_LOB0000146626C00004$$        USERS
TABLE               PERSON_VARR_TAB                  USERS
TYPE                PERSON_ADD
TYPE                PERSON_VARR

Elapsed: 00:00:00.09
test_user@10GR2>

  • Nested table ʹArrayʹ elements have no specific order. The data in the collection may be returned in a very different order than it was in when you inserted it. 
rajesh@10GR2> create type too as object(
  2     id number,
  3     dt date,
  4     data varchar2(20)
  5  );
  6  /

Type created.

Elapsed: 00:00:00.15
rajesh@10GR2> create type foo is table of too;
  2  /

Type created.

Elapsed: 00:00:00.18
rajesh@10GR2>
rajesh@10GR2> create table t(
  2     x number,
  3     y foo)
  4  nested table y store as t_y;

Table created.

Elapsed: 00:00:00.11
rajesh@10GR2>
rajesh@10GR2>
rajesh@10GR2> INSERT INTO t
  2    (
  3      x,
  4      y
  5    )
  6  SELECT level ,
  7    CAST(multiset
  8    (SELECT too(level,sysdate,rpad('*',20,'*'))
  9    FROM dual
 10      CONNECT BY level <= 1000
 11    ) AS foo)
 12  FROM dual
 13  CONNECT BY level <= 5;

5 rows created.

Elapsed: 00:00:00.07
rajesh@10GR2>
rajesh@10GR2> commit;

Commit complete.

Elapsed: 00:00:00.01
rajesh@10GR2>
rajesh@10GR2> SELECT x,
  2    tab.id
  3  FROM t ,
  4    TABLE(t.y) tab
  5  WHERE t.x   = 2
  6  AND rownum <= 5;

         X         ID
---------- ----------
         2        191
         2        192
         2        193
         2        194
         2        195

Elapsed: 00:00:00.01
rajesh@10GR2>
rajesh@10GR2>

  • VARRAYs are true arrays. The data will remain inherently ordered as you left it
rajesh@10GR2> create type too as object(
  2     id number,
  3     dt date,
  4     data varchar2(20)
  5  );
  6  /

Type created.

Elapsed: 00:00:00.12
rajesh@10GR2> create type foo is Varray(1000) of too;
  2  /

Type created.

Elapsed: 00:00:00.12
rajesh@10GR2>
rajesh@10GR2> create table t(
  2     x number,
  3     y foo);

Table created.

Elapsed: 00:00:00.09
rajesh@10GR2>
rajesh@10GR2> declare
  2     l_foo foo;
  3  begin
  4     for k in 1..5
  5     loop
  6             l_foo := foo();
  7             for i in 1..1000
  8             loop
  9                     l_foo.extend;
 10                     l_foo(l_foo.last) := too(i,sysdate,rpad('*',20,'*'));
 11             end loop;
 12     insert into t values (k,l_foo);
 13     end loop;
 14  end;
 15  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.07
rajesh@10GR2>
rajesh@10GR2>
rajesh@10GR2> commit;

Commit complete.

Elapsed: 00:00:00.04
rajesh@10GR2>
rajesh@10GR2> SELECT p.x,
  2    t_tab.id
  3  FROM t p,
  4  TABLE(p.y) t_tab
  5  where p.x = 2
  6  and rownum <= 5;

         X         ID
---------- ----------
         2          1
         2          2
         2          3
         2          4
         2          5

Elapsed: 00:00:00.00
rajesh@10GR2>
rajesh@10GR2>

No comments:

Post a Comment