Bookmark Fixed font Go to End

Doc ID: Note:139667.1
Subject: SUPPORT FOR MULTILEVEL COLLECTIONS IN ORACLE9I
Type: WHITE PAPER
Status: REVIEWED
Content Type: TEXT/PLAIN
Creation Date: 11-APR-2001
Last Revision Date: 25-JUN-2001
************************************************************* 

This article is being delivered in Draft form and may contain

errors. Please use the MetaLink "Feedback" button to advise

Oracle of any issues related to this article.

*************************************************************

PURPOSE

-------

The purpose of this bulletin is to describe support for multilevel collections

in Oracle9i and to provide examples of these new features. A secondary purpose

is to provide a short survey and examples of how this new functionality can be

very roughly approximated in versions prior to Oracle9i.

SCOPE & APPLICATION

-------------------

This document is intended to provide an introduction to multilevel collections

in SQL and PL/SQL. It is not necessarily comprehensive in its treatment though

it strives to be as complete as possible. In particular, no attention is given

to accessing multilevel collections via programmatic interfaces other than the

PL/SQL interface. This bulletin addresses issues that apply to all collections

(e.g. generic syntax not unique to a multilevel collection) only as background

and where it contributes to a better understanding of multilevel collections.

Finally, this document does not address Oracle9i collection enhancements which

are independent of whether the collections are nested (e.g. substitutability,

also known as heterogeneous collections).

Interested readers should consult the official Oracle9i documentation for any

additional information on this and related topics. See the References section

below for a list of relevant documentation.

SUPPORT FOR MULTILEVEL COLLECTIONS IN ORACLE9i

----------------------------------------------

Introduction

------------

In both Oracle8 and Oracle8i, it is possible to create collection types (nested

tables and VARRAYs) as full-fledged datatypes within the database. These types

can be used in many of the same contexts as built-in types; for example, they

can be used to declare columns in a table and PL/SQL variables can be declared

of a collection type. However, Oracle8 and Oracle8i supported only one level of

nesting for collections. For example, the following would not be permitted.

create or replace type TableType1 as table of number;

/

create or replace type TableType2 as table of TableType1;

/

The above example demonstrates an instance of direct nesting of collections, but

indirect nesting was also forbidden in Oracle8 and Oracle8i. For example, it was

not possible to create collections of objects having collection attributes.

create or replace type ObjWithCollection as object

(

keyval number,

atable TableType1

);

/

create or replace type TableType3 as table of ObjWithCollection;

/

The above example demonstrates indirect nesting of collections. This would also

fail in both Oracle8 and Oracle8i. A later section of this document will suggest

a workaround for this shortcoming and provide a short example of how this might

be implemented in pre-Oracle9i versions, but the workaround is far from ideal as

will become evident.

Therefore, Oracle9i was enhanced to provide direct support for both of the above

scenarios. Both SQL and PL/SQL were enhanced to support multilevel collections.

The remainder of this bulletin discusses those enhancements and provides a number

of examples that demonstrate the available functionality.

Why Multilevel Collections?

---------------------------

Although relational structures can be used to model virtually all requirements of

modern applications, there are many cases where the mapping from the application

model to a relational structure is not straightforward and can obscure the true

nature of the problem. This so-called 'impedence mismatch' or 'paradigm mismatch'

has implications on productivity since much of a developer's or DBA's time must

be spent mapping from one system to the other. Clearly, there are applications

that could benefit from the ability to directly model these 1:N relationships.

For example, an invoice may bill for multiple orders, each order may consist of

multiple items, and each orderable item could be composed of several components.

Multilevel collection semantics in particular have traditionally been modelled

using a series of parent and child tables linked via primary and foreign keys,

with the target object being constructed on-demand via a multitable join. With

multilevel collection support, storage of the nested table or VARRAY elements

happens transparently and access to elements occurs via implicit rather than

explicit joins. In many cases, this can simplify application development.

Creating Multilevel Collection Types

------------------------------------

The basic syntax for creating collection types (nested tables and VARRAYs) has

not changed; it remains essentially the same as in Oracle8 and Oracle8i.

However, the list of element types supported has been extended; in particular,

it is now possible to use other collection types or object types having one or

more collection attributes as the element type.

A number of examples that demonstrate some of the possible combinations follow.

Traditional Oracle8/Oracle8i Collections

----------------------------------------

create or replace type TableType as table of number;

/

create or replace type ArrayType as varray(5) of number;

/

Nested Table of Nested Table

----------------------------

create or replace type TableOfTableType as table of TableType;

/

Nested Table of VARRAY

----------------------

create or replace type TableOfArrayType as table of ArrayType;

/

VARRAY of Nested Table

----------------------

create or replace type ArrayOfTableType as varray(5) of TableType;

/

VARRAY of VARRAY

----------------

create or replace type ArrayOfArrayType as varray(5) of ArrayType;

/

This capability extends to an arbitrary number of levels of nesting with any

combination of nested tables and VARRAYs. For example, one might create yet

another nested table of the following form.

Nested Table of VARRAY of Nested Table

--------------------------------------

create or replace type TableOfArrayOfTableType as table of ArrayOfTableType;

/

It is also possible to create collections of objects containing one or more

collection attributes (which could also be multilevel collections).

For example,

create or replace type ObjectWithCollections as object

(

keyval number,

atable TableType,

anarray ArrayType,

multilevel TableOfArrayType

);

/

create or replace type TableOfObjects as table of ObjectWithCollections;

/

create or replace type ArrayOfObjects as varray(5) of ObjectWithCollections;

/

Creating Tables with Multilevel Collection Type Columns

-------------------------------------------------------

If multilevel collections are to be supported as full-fledged types, then

some mechanism must be provided to store them in the database. Furthermore,

manipulation of the collection should assume value semantics; that is, the

collection is treated as a single entity. Oracle9i fully supports storage

of multilevel collections in the database and manipulation via traditional

SQL mechanisms. This section will discuss the syntax used to create tables

containing multilevel collection columns. We also briefly discuss storage

models for nested tables and VARRAYs in this section.

First, consider the basic syntax used to create a table with a non-nested

collection type; we then expand this to include multilevel collections. A

collection type can be used to declare a column in a relational table and

can appear as an attribute in an object type which appears in a column in

a relational table or in an object table.

For example, consider the following scenarios involving a nested table.

Relational Table with Nested Table Column

-----------------------------------------

create table DBTableWithNestedTable

(

keyval number,

atable TableType

)

nested table atable store as DBTWNT_nt;

Object Table with Nested Table Attribute

----------------------------------------

create or replace type ObjectWithNestedTable as object

(

keyval number,

atable TableType

);

/

create table DBObjTabWithNestedTable of ObjectWithNestedTable

nested table atable store as DBOTWNT_nt;

The nested table storage clause determines the name of the table used to

store the elements of the nested table column or attribute. This storage

table is a normal database table except that it is implicitly associated

with the parent table via a system generated NESTED_TABLE_ID.

The nested storage table is implicitly created when the parent table is

created and is automatically dropped when the parent table is dropped.

The following example demonstrates the same basic concept using a VARRAY

rather than a nested table.

Relational Table with VARRAY Column

-----------------------------------

create table DBTableWithVarray

(

keyval number,

anarray ArrayType

)

varray anarray store as lob DBTWV_lob;

Object Table with VARRAY Attribute

----------------------------------

create or replace type ObjectWithVarray as object

(

keyval number,

anarray ArrayType

);

/

-- inline storage

create table DBObjTabWithVarray of ObjectWithVarray;

OR

-- LOB storage

create table DBObjTabWithVarray of ObjectWithVarray

varray anarray store as lob DBOTWV_lob;

If no LOB storage clause is specified, then the VARRAY is stored inline for

small values (<= 4000 bytes) and in a LOB for larger values. If a LOB storage

clause is specified, the VARRAY is always stored in a LOB, but it may be an

inline LOB unless 'storage in row' is disabled. Furthermore, a VARRAY stored

in a LOB will be stored in the same tablespace as the parent table unless a

different tablespace is specified via the LOB storage clause.

The syntax need only be modified slightly to support multilevel collections.

Since each element of a collection can also be another collection, storage

clauses (of the nested table or LOB variety) may also be nested.

Relational Table with Multilevel Collection Column

--------------------------------------------------

create table DBTableWithTableOfArray

(

keyval number,

multilevel TableOfArrayType

)

nested table multilevel store as DBTWTOA_nt

( varray column_value store as LOB DBTWOA_lob );

Note the nesting of the VARRAY storage clause for the nested VARRAY column.

Object Table with Multilevel Collection Attribute

-------------------------------------------------

create or replace type ObjectWithArrayOfTable as object

(

keyval number,

multilevel ArrayOfTableType

);

/

create table DBObjTabWithArrayOfTable of ObjectWithArrayOfTable

varray multilevel store as LOB DBOTWAOT_lob;

Note that in this latter case, no storage clause for the nested table was

required. The reason for this will become clearer when we discuss storage

models for nested tables and VARRAYs in the upcoming paragraphs.

Elements of nested tables that are either top-level or are also elements of

another nested table are stored out-of-line in a database table. Elements of

VARRAYs are always stored either inline or in a LOB, depending on the size of

the VARRAY and whether or not a LOB storage clause was specified when creating

the database table; this is true even for nested table elements of VARRAYs.

The below table provides an overview, for example, of how some of the types we

have created to this point would be stored in the database.

|----------------------------------------------------------------------------|

| Type | How is it stored? |

| | |

|------------------|---------------------------------------------------------|

| TableType | Stored in nested storage table. |

|------------------|---------------------------------------------------------|

| ArrayType | W/ LOB storage clause - Stored in inline LOB. |

| | wo/ LOB storage clause - Stored inline. |

| | |

| | *note: ArrayType can never be > 4000 bytes. |

| | |

| | *note: For arrays that can be > 4000 bytes, |

| | they would be stored in a LOB. |

|------------------|---------------------------------------------------------|

| TableOfTableType | Both table stored in nested storage tables. |

|------------------|---------------------------------------------------------|

| TableOfArrayType | Table stored in nested storage table. |

| | Array elements stored inline in the storage table. |

|------------------|---------------------------------------------------------|

| ArrayOfTableType | Array and all nested table elements in a single LOB. |

|------------------|---------------------------------------------------------|

| ArrayOfArrayType | w/ LOB storage clause - Stored in inline LOB. |

| | wo/ LOB storage clause - Stored inline. |

| | |

| | *note: ArrayOfArrayType can never be > 4000 bytes. |

| | |

| | *note: For arrays of arrays that can be > 4000 bytes, |

| | they would be stored in a LOB. |

|------------------|---------------------------------------------------------|

Obviously, there is the potential for any collection or nested collection to

become so large that it is impractical to return it to a client as a single

entity. For this reason, previous versions of Oracle supported the concept

of a locator; that is, rather than returning all of the elements of the

collection at once, a locator is returned and elements of the underlying

collection can be accessed iteratively via this locator.

By default, value semantics are used when returning collections. Therefore,

you must ask that a collection be returned by locator. There are two ways

to accomplish this; the RETURN AS LOCATOR clause to CREATE TABLE and the

/*+ NESTED_TABLE_GET_REFS +*/ hint. For example, one of the tables from

a previous section of this bulletin could have been created as follows.

Relational Table with Nested Table Column Returned as Locator

-------------------------------------------------------------

create table DBTableWithNestedTable

(

keyval number,

atable TableType

)

nested table atable store as DBTWNT_nt

return as locator;

Or the table can be created as normal and a hint can be provided when the

table is queried as follows.

Relational Table with Nested Table Column

-----------------------------------------

create table DBTableWithNestedTable

(

keyval number,

atable TableType

)

nested table atable store as DBTWNT_nt;

-- query the nested table as a locator

select /*+ NESTED_TABLE_GET_REFS +*/ atable

from DBTableWithNestedTable

where keyval = 1;

In Oracle9i, this concept has been extended to allow returning collections

at any level of nesting using a locator. For example,

Relational Table with Top-Level Collection Returned as Locator

--------------------------------------------------------------

create table DBTableWithTableOfTable

(

keyval number,

multilevel TableOfTableType

)

nested table multilevel store as DBTWTOT_nt

( nested table column_value store as DBTWTOT_nt2 )

return as locator;

In this case, only the top-level table is returned as locator, but it is

possible to return nested tables as locators from any or all levels. For

example, both levels could be returned as locator using the following.

Relational Table with Collections Returned as Locators

------------------------------------------------------

create table DBTableWithTableOfTable

(

keyval number,

multilevel TableOfTableType

)

nested table multilevel store as DBTWTOT_nt

( nested table column_value store as DBTWTOT_nt2

return as locator )

return as locator;

See the below section on PL/SQL for information about using these returned

locators to retrieve the underlying collection elements from the database.

NOTE: Returning locators for a VARRAY is not supported in the initial

release of Oracle9i, but may be added in a future release.

Finally, each storage table (for nested tables) or LOB (for VARRAYs) can be

assigned its own storage parameters when the table is created. This can be

useful for specifying that a table or LOB should be stored in a different

tablespace or modifying physical storage attributes for a table or LOB.

In addition, it is possible to add constraints to and/or create indexes on

the columns appearing in a nested storage table. Most storage parameters,

constraints, and indexes can also be modified, added, dropped, or rebuilt

via the appropriate ALTER command after the creation of the storage table,

subject to the same limitations that apply to other database tables.

Manipulation of Multilevel Collection Types in SQL

--------------------------------------------------

Multilevel collection columns (or attributes of object in an object table)

can be manipulated via SQL using extensions to traditional SQL syntax. The

following paragraphs will discuss the required syntax and provide a number

of examples based on the types and tables created in previous sections.

A default constructor is created for every object type and this constructor

can be used in SQL to create an object of the appropriate type. The default

constructor has the same name as the type and has input parameters of the

same number and type as the attributes of the object type. Constructors for

collection types also have the same name as the type, but accept a variable

argument list, one argument for each element of the collection. Furthermore,

for multilevel collections, each element can be another collection.

The first obvious scenario in which a constructor might be needed is during

the insertion of a row into a table (relational table with object column or

object table). For example,

Insert into Relational Table with Object Column

-----------------------------------------------

insert into DBTableWithTableOfArray

values ( 1, TableOfArrayType

(

ArrayType( 1, 2, 3 ),

ArrayType( 4, 5, 6 )

)

);

Insert into Object Table

------------------------

insert into DBObjTabWithArrayOfTable

values ( ObjectWithArrayOfTable

( 1, ArrayOfTableType

(

TableType( 1, 2 ),

TableType( 3, 4 ),

TableType( 5, 6 )

)

)

);

The above examples both insert a collection as an atomic entity into some

top-level database table. For nested table columns and attributes, it is

also possible to perform piecewise inserts into a nested collection.

NOTE: VARRAYS do not support piecewise operations. You must insert and/or

update VARRAYS as a single, atomic unit.

For example,

Insert into Nested Collection - Example 1

-----------------------------------------

insert into DBTableWithTableOfTable

values ( 1, TableOfTableType ( TableType ( 1, 2 ) ) );

insert into TABLE( select dbt.multilevel

from DBTableWithTableOfTable dbt

where dbt.keyval = 1 )

values ( TableType ( 3, 4 ) );

Note that the TABLE operator is used to convert the nested table returned

by the nested SQL statement into a form usable for inserts. This concept

can be extended to an arbitrary number of levels of nesting. For example,

Insert into Nested Collection - Example 2

-----------------------------------------

create or replace type KeyedObject as object

(

keyval number,

mycoll TableType

);

/

create or replace type TableOfKeyedObject as table of KeyedObject;

/

create or replace type KeyedObjectWithMulti as object

(

keyval number,

mycoll TableOfKeyedObject

);

/

create table DBTableWithTableOfKeyedObject of KeyedObjectWithMulti

nested table mycoll store as DBTWTOKO_nt (

nested table mycoll store as DBTWTOKO_nt2 );

insert into DBTableWithTableOfKeyedObject

values ( KeyedObjectWithMulti

( 1, TableOfKeyedObject

(

KeyedObject( 1, TableType( 1, 2 ) ),

KeyedObject( 2, TableType( 3, 4 ) )

)

)

);

insert into TABLE( select dbt2.mycoll

from TABLE( select dbt1.mycoll

from DBTableWithTableOfKeyedObject dbt1

where dbt1.keyval = 1 ) dbt2

where dbt2.keyval = 2 )

values ( 5 );

The above examples also illustrate how a select can be used to access a

nested table, but to further clarify, consider the following SQL which

selects the values from the nested table into which we inserted above.

Select from a Nested Collection

-------------------------------

select dbt3.column_value

from TABLE( select dbt2.mycoll

from TABLE( select dbt1.mycoll

from DBTableWithTableOfKeyedObject dbt1

where dbt1.keyval = 1 ) dbt2

where dbt2.keyval = 2 ) dbt3;

NOTE: The reserved keyword COLUMN_VALUE is used when accessing a table

of a scalar type since elements in this case do not have a name.

Although the above query permits one to view the contents of the nested

table, it might be nice to see how this data corresponds to data stored

in the parent table or collection.

In Oracle8i, collection unnesting (also known as flattened subquery) was

introduced to meet this need. In Oracle9i, this functionality has been

extended to support multilevel collections. For example,

Unnesting of Multilevel Collections

-----------------------------------

select dbt1.keyval, dbt2.keyval, dbt3.column_value

from DBTableWithTableOfKeyedObject dbt1,

TABLE(dbt1.mycoll) dbt2,

TABLE(dbt2.mycoll) dbt3;

NOTE: Although piecewise updates, deletes and inserts are only permitted

on nested tables, basic selects and collection unnesting are both

supported for VARRAYs as well.

Updates and deletes are performed in a manner analogous to the above, but

for completeness, several examples follow that demonstrate the syntax for

performing updates and deletes against nested table columns or attributes.

Updating a Multilevel Collection

--------------------------------

update TABLE ( select dbt2.mycoll

from TABLE( select dbt1.mycoll

from DBTableWithTableOfKeyedObject dbt1

where dbt1.keyval = 1 ) dbt2

where dbt2.keyval = 2 ) dbt3

set dbt3.column_value = 7

where dbt3.column_value = 3;

Deleting from a Multilevel Collection

-------------------------------------

delete from TABLE ( select dbt2.mycoll

from TABLE( select dbt1.mycoll

from DBTableWithTableOfKeyedObject dbt1

where dbt1.keyval = 1 ) dbt2

where dbt2.keyval = 2 ) dbt3

where dbt3.column_value = 4;

Manipulation of Multilevel Collection Types in PL/SQL

-----------------------------------------------------

Support for multilevel collections has also been added to Oracle9i PL/SQL.

Multilevel collections can be constructed and accessed in the same manner

as non-nested collections, with only a few minor extensions to the syntax.

Constructing a multilevel collection in PL/SQL is essentially the same as

in SQL. For example, the following code snippet declares and initializes

a multilevel collection of type TableOfArrayOfTableType.

declare

multi TableOfArrayOfTableType;

begin

multi := TableOfArrayOfTableType

(

ArrayOfTableType(

TableType( 1, 2 ),

TableType( 3, 4 )

),

ArrayOfTableType(

TableType( 5, 6 ),

TableType( 7, 8 ),

TableType( 9, 0 )

)

);

end;

/

The basic rules for assignment compatibility and comparison are unchanged

from Oracle8 and Oracle8i. In particular, direct omparison of collections

or objects with collection attributes is not supported and the assignment

of collections and collection elements requires only that both the source

and destination be of the same type. Implicit casting is not supported.

Elements of a multilevel collection are accessed using subscript notation

similar to that used for non-nested collections. The existing syntax has

been extended to allow for multiple subscripts and to support insertion

of attribute names when dealing with objects with collection attributes.

Accessing Elements of a Multilevel Collection - Scalar Tables Only

------------------------------------------------------------------

declare

mynum number;

mytab TableType;

multi TableOfTableType;

begin

multi := TableOfTableType (

TableType( 1, 2, 3),

TableType( 4, 5, 6),

TableType( 7, 8, 9)

);

mytab := multi(2); -- TableType( 4, 5, 6)

mynum := mytab(1); -- 4

mynum := multi(2, 1); -- 4

end;

/

Accessing Elements of a Multilevel Collection - With Object Tables

------------------------------------------------------------------

declare

mynum number;

myarr ArrayType;

mytab TableType;

multi TableOfObjects;

begin

multi := TableOfObjects

(

ObjectWithCollections

(

1,

TableType( 1, 2, 3),

ArrayType( 4, 5, 6),

TableOfArrayType

(

ArrayType( 7, 8, 9)

)

),

ObjectWithCollections

(

2,

TableType( 9, 8, 7),

ArrayType( 6, 5, 4),

TableOfArrayType

(

ArrayType( 3, 2, 1)

)

)

);

mytab := multi(1).atable; -- TableType( 1, 2, 3)

myarr := multi(2).anarray; -- ArrayType( 6, 5, 4)

mynum := mytab(3); -- 3

mynum := multi(1).atable(3); -- 3

mynum := myarr(1); -- 6

mynum := multi(2).anarray(1); -- 6

mynum := multi(1).multilevel(1)(2); -- 8

end;

/

The above access methods are intended for use with collection values, not

collection locators. A different method is required for accessing elements

of a collection returned as a locator.

First, it must be possible to determine whether a returned value is in fact

a locator or a value. This becomes necessary because the decision to return

a locator is made based on factors which may be outside the control of the

PL/SQL application (e.g. the table was created with RETURN AS LOCATOR).

As was the case for Oracle8 and Oracle8i, the UTL_COLL provided package is

used to determine whether a returned collection represents a locator or an

actual collection by-value.

Second, it must be possible to retrieve the elements using the locator. This

is also accomplished in the same manner as for non-nested collections under

Oracle8 and Oracle8i. However, for clarity a complete example follows.

declare

anum number;

tab TableType;

taboftab TableOfTableType;

-- retrieve nested table using a locator for parent

cursor nestedtabs is

select tot.column_value

from TABLE(CAST(taboftab AS TableOfTableType)) tot;

-- retrieve numbers using a locator for the table

cursor numbers is

select t.column_value

from TABLE(CAST(tab AS TableType)) t;

procedure process_tab is

begin

-- if this is a locator process as such

if ( UTL_COLL.IS_LOCATOR(tab) ) then

open numbers;

loop

fetch numbers into anum;

exit when numbers%NOTFOUND;

dbms_output.put_line('anum = ' || anum);

end loop;

close numbers;

else -- otherwise, use normal subscripts

for i in 1..tab.COUNT loop

anum := tab(i);

dbms_output.put_line('anum = ' || anum);

end loop;

end if;

end process_tab;

begin

-- retrieve the top-level collection object

select dbt.multilevel into taboftab

from DBTableWithTableOfTable dbt

where dbt.keyval = 1;

-- if top-level table is locator process as such

if ( UTL_COLL.IS_LOCATOR(taboftab) ) then

open nestedtabs;

loop

fetch nestedtabs into tab;

exit when nestedtabs%NOTFOUND;

process_tab;

end loop;

close nestedtabs;

else -- otherwise, use normal subscripts

for i in 1..taboftab.COUNT loop

tab := taboftab(i);

process_tab;

end loop;

end if;

end;

/

You will note from the above example that locators must be accessed via

SQL (i.e. the collection must be cast appropriately and elements must be

fetched rather than assigned). Obviously, writing code that can deal with

any combination of locators and values in a multilevel collection can be

tedious. Therefore, it is probably wise to agree in advance on when and

where locators will be used. In any event, it is possible to make code

smart enough to deal with any situation (as demonstrated by the above).

Support for Multilevel Collections in Object Views

--------------------------------------------------

The following DDL demonstrates how to create an object view equivalent to

the DBTableWithTableOfKeyedObject object table created above. Note that

this example is not particularly realistic, but does demonstrate much

of the syntax used in creating object views with multilevel collections.

Relational Tables Assumed to Already Exist

------------------------------------------

create table DBRelTable1 ( pk number, fk number );

create table DBRelTable2 ( pk number, fk number );

create table DBRelTable3 ( pk number, val number );

Creating the Object View

------------------------

create view DBViewWithTableOfKeyedObject

of KeyedObjectWithMulti

with object oid ( keyval ) as

select KeyedObjectWithMulti

( rt1.pk,

CAST( MULTISET

(select rt2.pk,

CAST( MULTISET

(select rt3.val

from DBRelTable3 rt3

where rt3.pk = rt2.fk )

AS TableType )

from DBRelTable2 rt2

where rt2.pk = rt1.fk )

AS TableOfKeyedObject )

)

from DBRelTable1 rt1;

Although object views of this type, like many other views, may not be

directly updatable, it is possible to support updates via an 'INSTEAD

OF' trigger. However, the details of doing so are not covered here.

Emulating Multilevel Collection in Oracle8 and Oracle8i

-------------------------------------------------------

Because multilevel collections are not directly supported in Oracle8 and

Oracle8i, object models requiring this capability must use object REF's

to nest collections inside other collections. This section will briefly

outline how this can be achieved and provide a short example, including

type and table DDL as well as representative SQL that demonstrates how

one might access information stored in nested collections.

First, the required object types must be created. Because we need to use

REF's to a collection everywhere a collection would be used in Oracle9i,

and because it is not possible to create a REF directly on a collection,

it is necessary to 'wrap' nested collections in an object type.

Create the Object and Collection Types

--------------------------------------

create or replace type WorkaroundObject as object

(

attr1 number,

attr2 varchar2(10)

);

/

create or replace type InnerNestedTable as table of WorkaroundObject;

/

create or replace type WrapperObject as object

(

pk number,

nt InnerNestedTable

);

/

NOTE: The addition of the 'pk' attribute is a matter of convenience as it

simplifies formulation of the DML for populating the tables. Such

'key' columns may or may not be required depending on the nature

of your application and data.

create or replace type OuterNestedTable as table of REF WrapperObject;

/

Create the Database Tables

--------------------------

create table DBWrapperTable of WrapperObject

nested table nt store as DBWT_nt;

create table DBTableWithWorkaround

(

pk number,

nt OuterNestedTable

)

nested table nt store as DBTWW_nt;

Populate the Database Tables with Data

--------------------------------------

insert into DBWrapperTable

values ( WrapperObject

( 1,

InnerNestedTable( WorkaroundObject( 1, 'one' ),

WorkaroundObject( 2, 'two' ),

WorkaroundObject( 3, 'three' ) )

)

);

insert into DBWrapperTable

values ( WrapperObject

( 2,

InnerNestedTable( WorkaroundObject( 4, 'four'),

WorkaroundObject( 5, 'five' ),

WorkaroundObject( 6, 'six' ) )

)

);

insert into DBTableWithWorkaround

values ( 1,

CAST( MULTISET( select ref(dbw)

from DBWrapperTable dbw

where dbw.pk between 1 and 2 )

AS OuterNestedTable )

);

Select Data from a Nested Collection Element

--------------------------------------------

select dbw3.attr2

from TABLE( select dbw2.column_value.nt

from TABLE( select dbw.nt

from dbtablewithworkaround dbw

where dbw.pk = 1 ) dbw2

where dbw2.column_value.pk = 1 ) dbw3

where dbw3.attr1 = 1

References

----------

For an overview of object-relational features in Oracle8 and up

Note: 69467.1

For information on support for objects and collections in Oracle9i

Oracle9i SQL Reference

PL/SQL User's Guide and Reference

Oracle9i Supplied PL/SQL Packages Reference

Oracle9i Application Developer's Guide - Object-Relational Features

.

Copyright (c) 1995,2000 Oracle Corporation. All Rights Reserved. Legal Notices and Terms of Use.