You are on page 1of 26

Chapter 11

OBJECT-RELATIONAL
ORACLE8 AND PL/SQL8
“Civilization advances by extending the number of important operations
which we can perform without thinking about them.”
Alfred North Whitehead

◆ Overview of the Object-Relational Technology

◆ Object Types and Object Instances

◆ Storing Objects in the Oracle8 Database

◆ Collections

◆ Summary

319
320 Chapter 11

Chapters 8 and 9 presented an overview of the traditional SQL and PL/SQL lan-
guages as they have existed and been used with the Oracle Server database before
the release of version 8. This release pushed the Oracle Server technology in three
major directions:

❏ Scalability. Improvements in this direction were aimed at increasing the


support of the Oracle Server database for data warehouses and Very Large
Database (VLDB) applications.
❏ Openness. Improvements in this area were aimed at making the Oracle Serv-
er the centerpiece of Oracle Corporation’s Network Computing Architecture
(NCA).
❏ Object-Relational technology. Developments in this area expanded the tra-
ditional relational framework of Oracle and added support for user-defined
object types and collections.

This chapter will focus on the major concepts introduced by Oracle8 and its
versions of SQL and PL/SQL to support the object-relational technology.

11.1 Overview of the Object-Relational Technology

As discussed in Chapter 9, the relational model was defined in the 1970s and
commercial applications of it started being implemented in the 1980s. Through-
out that decade and in the early to mid-1990s, relational databases grew up and
hosted a large percentage of the data corporations cared about. The simplicity of
the relational world (expressed in a short list of steps known as the normaliza-
tion rules), the straightforward implementations of SQL by all the database ven-
dors, and a number of data modeling and database design tools helped IT pro-
fessionals put order in their data universe and build robust relational database
applications.
The relational model, as defined and described in its latest standard (SQL2),
considers the data to be organized in fields and records. The data stored in each
of these fields may be described by a data type from a (short) list of predefined
data types. By the mid-1990s, an increasing number of IT professionals began
realizing that what was considered a virtue of the relational model—its simplic-
ity—was quickly becoming a liability. The fast-paced improvements in hard-
ware, software, and communication technologies enabled corporations to
address much more complex structures and relations in their data. The relation-
al model by itself was no longer optimal for storing and handling this complex-
Object-Relational Oracle8 and PL/SQL8 321

ity. There were two alternatives from which system designers and application
developers could choose in order to address the shortcomings of the relational
databases:

❏ Build an object-oriented layer on top of the pure relational model. This


approach placed the burden on the application developers and increased the
complexity of applications by introducing a middle layer in the code whose
purpose was to abstract and encapsulate the relational storage of data.
❏ Migrate the data from a relational database to an object-oriented database.
This approach required abandoning the investments on relational database
products and its success depended on the migration of legacy data to the new
environments.

Clearly, none of these approaches was very satisfying. By the mid-1990s, all the
stakeholders in the relational world, including database vendors, standards’ com-
mittees, designers, and developers, agreed that the relational model and SQL had to
be extended with object-oriented features. In Oracle8, the Oracle Corporation intro-
duced a set of features that enabled designers and developers to natively implement
and support objects in the Oracle Server. These features are collectively known as
the Objects Option of Oracle8 and are grouped in the following categories:

❏ Object types. They are also known as user-defined or abstract data types.
Object types allow you to represent the structure (attributes) and the behav-
ior (methods) of entities as they appear in the real world.
❏ References. They are handles to instances of object types, which allow you to
represent the relations among objects in a way that optimizes the storage of
these objects.
❏ Collections. They allow you to group data in an ordered or unordered way
for efficient and optimized access. These are two categories of collections in
Oracle8 Objects Option: variable-length arrays, often abbreviated as
VARRAYs, and nested tables.
❏ Object storage. Oracle8 with Objects Option allows you not only to define
objects from the previous three categories, but also to store them in the Oracle
Server. Extensions to SQL allow you to manipulate these objects just like the
predefined data types. Furthermore, object views allow you to build object
definitions upon purely relational structures and are very important when
migrating applications into the object-relational world.

In the remainder of this chapter, each of these categories of features will be dis-
cussed in detail. From now on, the expression Oracle8 means Oracle8 with Objects
Option, unless otherwise noted.
322 Chapter 11

11.2 Object Types and Object Instances

The world around us is made up of objects which we see, touch, and interact with
constantly. These objects are materialized in the form of a number of attributes
and operations you perform with them. A simple object like a door has attributes
such as weight, material, color, dimensions, and price. Some operations you can
perform on a door are open, close, mount, lock, and break. Depending on who
you are and your intentions, you may be interested in different attributes of
doors and perform different operations on them. If you are a homeowner consid-
ering purchasing a door for your bedroom, you may want to know its dimensions
and price, as well as how to open, close, or lock it. But, if you are a contractor
hired to install a door, you need to know its weight and material, as well as how
to mount it.
One of the major advantages that object-oriented languages brought to soft-
ware engineering is the ability to model, design, and build applications using the
familiar object paradigm with which we all are familiar. These languages allow
you to distinguish between the actual objects and the templates that describe the
structure and methods of a group of objects. In a number of object-oriented lan-
guages, like C++ and Java, these templates are called object classes, or simply class-
es. Class instances created and manipulated during the life of the application are
the actual objects with which users interact.
In Oracle8, the templates that describe the attributes and methods of a catego-
ry of objects are called object types. They are defined using DDL commands and
considered part of the schema objects defined in the Oracle Server data dictionary.
The actual objects are called instances of the object type and can be stored tempo-
rarily in PL/SQL program units or persistently in the Oracle Server database struc-
tures. Object instances can be created and maintained using SQL DML commands
and PL/SQL syntactic structures.
In a sense, PL/SQL and Oracle have offered similar constructs even before
Oracle8. In fact any language offers a number of “object types” called data types.
For example, the PL/SQL data types VARCHAR2 or NATURAL define the
attributes of their instance variables, such as alphanumeric strings no longer than
2000 characters or positive integers. They also define the allowed operations on
each of these variables—NATURAL numbers can be added and multiplied,
whereas VARCHAR2 strings cannot. Complex data types in PL/SQL, such as
records and index-by tables discussed in the previous chapter, allow you to extend
the predefined scalar data types with complex data types that are associated with
a number of predefined methods, like NEXT and PRIOR in the case of index-by
tables. Finally, packages in PL/SQL have for a long time allowed the association
Object-Relational Oracle8 and PL/SQL8 323

and encapsulation of data (attributes) with methods. The Objects Option in


Oracle8 unifies and extends these features in the following aspects:

❏ Support for abstract data types (ADT). The object types created in Oracle8
can be of any level of complexity. They may contain a large number of
attributes (up to 1000) and methods. Each of these attributes may be of a pre-
defined or another abstract data type.
❏ Centralized storage of ADT definitions. By storing the definitions and prop-
erties of the object types as part of the schema, Oracle8 enables you to access
and use them at any level in your application architecture. You can use them
at the database level, or through PL/SQL at the application layer. Further-
more, mechanisms provided by other Oracle Corporation’s tools, such as
Designer/2000, allow you to derive class definitions in languages such as
C++ from object type definitions stored in the Repository.

11.2.1 DEFINING OBJECT TYPES


As mentioned earlier, object types are defined as part of the schema in the Ora-
cle Server using DDL statements. The structure of object types is very similar to
that of the packages discussed in the previous chapter. Like packages, object
types are made up of a mandatory specification and an optional body part. The
specification part contains the attributes and the method specifications for the
object type. The body part contains the implementation of these methods. How-
ever, unlike packages, object types cannot contain private attributes or methods
implemented in the body that are not defined in the specification. Structurally,
each object type may contain one or more attributes and zero or more methods.
More details on attributes and methods are provided in the following section.
The SQL statement shown in Figure 11.1 creates the specification for the object
type point_t used to store information about points in the two-dimensional
real plan. This object type definition allows you to store the coordinates of the
point and provides a method for computing its distance from the origin of coor-
dinates.

CREATE TYPE point_t AS OBJECT (


x NUMBER,
y NUMBER,
MEMBER FUNCTION GetDistance RETURN NUMBER
);

FIGURE 11.1 A simple example of an object type definition.


324 Chapter 11

The statement shown in Figure 11.2 creates the corresponding body of the
object type point_t.

CREATE TYPE BODY point_t AS


MEMBER FUNCTION GetDistance RETURN NUMBER IS
BEGIN
RETURN SQRT(x*x + y*y);
END;
END;

FIGURE 11.2 The implementation of the object type point_t.

11.2.2 ATTRIBUTES AND METHODS


The attributes in an object type describe the placeholders in which object instances
will store data. As you can see from the examples at the end of the previous sec-
tion, attributes are defined in a way similar to the way variables are defined in a
PL/SQL program unit. However, certain differences exist, as detailed in the fol-
lowing list:

❏ Attributes cannot be initialized in the object type specification.


❏ Attributes cannot be constrained in the object type specification other than
by the data type and length (or precision, for numeric data types that sup-
port it).
❏ Most of the Oracle’s predefined data types can be used as an attribute’s data
type. Some exceptions are the data types LONG, LONG RAW, BOOLEAN,
RECORD, and BINARY_INTEGER.

On the other hand, an attribute may be assigned any user-defined object type
stored in the Oracle Server schema. This mechanism allows you to create complex
objects with attributes defined as other nested objects. The statements shown in
Figure 11.3 contain the specification and body for the object type circle_t. As
you can see from this example, the center of the circle is an object of type point_t
defined at the end of the previous section.
The object type definitions shown so far contain only one simple method.
However, methods are not required to define an object type. In this case, the body
of the type definition is not required and the object type resembles a structure in
languages like C. Obviously, the benefits of encapsulating data with operations are
realized when the attributes are followed by a number of interesting methods in
the object type definition. The statement contained in Figure 11.4 extends the def-
inition of the object type point_t with a number of methods that implement oper-
ations with point on the Real plane.
Object-Relational Oracle8 and PL/SQL8 325

CREATE OR REPLACE TYPE circle_t AS OBJECT (


center point_t,
radius NUMBER,
MEMBER FUNCTION GetArea RETURN NUMBER
);

CREATE OR REPLACE TYPE BODY circle_t AS


MEMBER FUNCTION GetArea RETURN NUMBER IS
pi CONSTANT NUMBER := 3.14;
BEGIN
RETURN (pi*radius*radius);
END;
END;

FIGURE 11.3 A simple example of a nested object type definition and its implementation.

CREATE OR REPLACE TYPE point_t AS OBJECT (


xNUMBER,
yNUMBER,
MEMBER FUNCTION GetDistance RETURN NUMBER,
MEMBER FUNCTION GetDistance (p point_t) RETURN NUMBER,
MEMBER FUNCTION GetMidPoint RETURN point_t,
MEMBER FUNCTION GetMidPoint (p point_t) RETURN point_t,
MEMBER FUNCTION AddPoint (p point_t) RETURN point_t,
MEMBER FUNCTION SubtractPoint (p point_t) RETURN point_t
);

FIGURE 11.4 The complete definition of the object type point_t.

The corresponding body of this object type is shown in Figure 11.5. From the
contents of this figure, you can see that the methods of an object type are defined
and implemented just like regular functions and procedures in a PL/SQL pack-
age. In particular, the specification of the method resides within the object type
specification, whereas its implementation resides in the object type body. Fur-
thermore, methods can be overloaded as in the example of the methods GetDis-
tance. Based on the arguments passed to these methods, a client application is
able to compute the distance of a point from the origin of the coordinate system
or from another point on the plane. Similarly, the first method GetMidPoint
allows you to compute the coordinates of the point in the middle of the segment
that joins the given point with the origin of coordinates. The second method com-
putes the midpoint of the segment that connects the point with any other point
on the plane.
326 Chapter 11

CREATE OR REPLACE TYPE BODY point_t AS


MEMBER FUNCTION GetDistance RETURN NUMBER IS
BEGIN
RETURN SQRT(x*x + y*y);
END;

MEMBER FUNCTION GetDistance (p point_t) RETURN NUMBER IS


BEGIN
RETURN SQRT((x-p.x)*(x-p.x) + (y-p.y)*(y-p.y));
END;

MEMBER FUNCTION GetMidPoint RETURN point_t IS


new_p point_t := point_t(NULL,NULL);
BEGIN
new_p.x := x/2;
new_p.y := y/2;
RETURN new_p;
END;

MEMBER FUNCTION GetMidPoint (p point_t) RETURN point_t IS


new_p point_t := point_t(NULL,NULL);
BEGIN
new_p.x := (x + p.x)/2;
new_p.y := (y + p.y)/2;
RETURN new_p;
END;

MEMBER FUNCTION AddPoint (p point_t) RETURN point_t IS


new_p point_t := point_t(NULL,NULL);
BEGIN
new_p.x := SELF.x + p.x;
new_p.y := SELF.y + p.y;
RETURN new_p;
END;

MEMBER FUNCTION SubtractPoint (p point_t) RETURN point_t IS


new_p point_t := point_t(NULL,NULL);
BEGIN
new_p.x := x - p.x;
new_p.y := y - p.y;
RETURN new_p;
END;
END;

FIGURE 11.5 The complete implementation of the object type point_t.


Object-Relational Oracle8 and PL/SQL8 327

The implementations of the methods for the object type point_t shown in
Figure 11.5 highlight a few special characteristics of them, which are described in
the following list.

❏ Constructor method. The implementation of the last four methods contains a


call to the method point_t, which was never defined explicitly. This is a spe-
cial method, called a constructor method, provided automatically by Oracle
for every object type you define. The constructor allows you to create an
object instance and, in the process, initialize all its data attributes. Structural-
ly, the constructor method is a function with the same name as the object type,
which takes as formal arguments all the attributes of the object type in the
order in which they are defined and returns an object instance of that type.
❏ SELF. A method may have to operate with instances of the same object type,
as is the case with the methods Add or Substract. In such cases how does
PL/SQL distinguish the members of the object instance whose method was
called from those of the other objects? PL/SQL provides each object instance
with a reference to itself, called SELF. SELF is not an object instance in itself;
it is passed to each method as an implicit argument, and therefore it can be
accessed from within the method just like any other object, as shown in the
case of the method Add. If you are familiar with object-oriented languages
like C++ or Java, you will notice that SELF is very similar to the reference
this in these languages. One more point of similarity is that SELF is not
required to reference the members of an object within a method. As shown in
the case of function Subtract, an attribute or method without an object ref-
erence is implicitly considered as a member of the object instance whose
method is invoked.

In most cases, SELF is passed to the method implicitly by the


PL/SQL engine. However, PL/SQL allows you to pass the SELF
reference explicitly as well as the first formal parameter of a
method. Practically, the only time you may want to pass the ref-
erence SELF explicitly is when you want to change its mode. By
default, the mode of this parameter is IN for functions and IN
OUT for procedures. Changing this mode is rarely necessary and should be
questioned seriously.

By default, PL/SQL allows you to compare two instances of the same object
type in the sense that it can tell you whether these instances are equal or not. In
addition, you can also add the ability to sort these objects just as you sort objects of
basic data types, such as DATE, NUMBER, or VARCHAR2. In order for Oracle to
be able to sort object instances, the object type must contain either one of the fol-
lowing two methods:
328 Chapter 11

❏ MAP method. This method is a function that maps the object instance to a
hash value, which is then used to sort the object. The data type of the return
value of the map function can be DATE, NUMBER, or VARCHAR2. The key-
word MAP should precede the mapping function in the object type definition
and body. Figure 11.6 shows the object type point_t discussed earlier in
which the function GetDistance is defined as a MAP function. Given the
implementation of this function, you can easily see that point objects will be
ordered based on their distance from the origin of the coordinate system.
❏ ORDER method. An alternative way to sort objects is to define a method that
compares the object with another object. This method is preceded by the key-
word ORDER and returns a negative number, zero, or a positive number if
the object whose method is invoked is less than, equal, or greater than the
input object. Figure 11.7 shows the definition of the object type point_t
with the ORDER method Compare. Figure 11.8 shows the simple, one-line
implementation of this method. When sorting a set of objects using the
ORDER method, Oracle needs to compare them individually. For large sets,
this may degrade the performance of the operation; therefore you should
consider using the MAP function.

CREATE TYPE point_t AS OBJECT (


x NUMBER;
y NUMBER;
MAP MEMBER FUNCTION GetDistance RETURN NUMBER,
MEMBER FUNCTION GetDistance (p point_t) RETURN NUMBER,
MEMBER FUNCTION GetMidPoint RETURN point_t,
MEMBER FUNCTION GetMidPoint (p point_t) RETURN point_t,
MEMBER FUNCTION AddPoint (p point_t) RETURN point_t,
MEMBER FUNCTION SubtractPoint (p point_t) RETURN point_t
);

FIGURE 11.6 The object type point_t with a MAP function.

CREATE TYPE point_t AS OBJECT (


x NUMBER;
y NUMBER;
MEMBER FUNCTION GetDistance RETURN NUMBER,
MEMBER FUNCTION GetDistance (p point_t) RETURN NUMBER,
MEMBER FUNCTION GetMidPoint RETURN point_t,
MEMBER FUNCTION GetMidPoint (p point_t) RETURN point_t,
MEMBER FUNCTION AddPoint (p point_t) RETURN point_t,
MEMBER FUNCTION SubtractPoint (p point_t) RETURN point_t
ORDER MEMBER FUNCTION Compare (p point_t) RETURN NUMBER
);

FIGURE 11.7 The object type point_t with an ORDER function.


Object-Relational Oracle8 and PL/SQL8 329

ORDER MEMBER FUNCTION Compare(p point_t) RETURN NUMBER IS


selfDistance NUMBER;
pDistance NUMBER;
BEGIN
selfDistance := x*x + y*y;
pDistance := p.x*p.x + p.y*p.y;
RETURN (selfDistance - pDistance);
END;

FIGURE 11.8 The implementation of the ORDER method Compare of the object type point_t.

The discussion so far could apply to any language that supports objects. How-
ever, what is special about the Object Option in Oracle8 is that object instances
may be closely associated with objects stored in the database. Therefore, the meth-
ods defined in the object type could be part of SQL statements like SELECT,
INSERT, or UPDATE. In order for these statements to be executed successfully,
the member methods have to follow a small number of rules aimed at limiting
their references to and dependency from other schema objects, like tables and
stored packages. For example, a method cannot be invoked from a SQL statement
if it inserts, updates, or deletes data from one or more tables. You can enlist the
PL/SQL compiler to help you define and implement such methods. The pragma
RESTRICT_REFERENCES indicates to the compiler a method and the level of
restrictions placed on it. When the compiler compiles this method, it uses the prag-
ma definition to verify that the method is not violating any of the rules defined by
the pragma. Figure 11.9 shows how you can inform the compiler that the function
GetArea should Read No Database State (RNDS), Write No Database State
(WNDS), Read No Package State (RNPS), and Write No Package State (RNPS).

CREATE TYPE circle_t AS OBJECT (


centerpoint_t,
radiusNUMBER,
MEMBER FUNCTION GetArea RETURN NUMBER,
PRAGMA RESTRICT_REFERENCES(GetArea, RNDS, WNDS, RNPS, WNPS)
);

FIGURE 11.9 Using the pragma RESTRICT_REFERENCES.

11.2.3 WORKING WITH OBJECT INSTANCES


In order to create instances of objects in PL/SQL program units or even store them
in database objects, you must first create their object types in the schema as
described in the previous two sections. Then you can declare and initialize objects
and access their attributes and methods. Figure 11.10 shows the contents of a
Developer/2000 PL/SQL procedure which takes as arguments the coordinates of
two points on the plane (X1, Y1, X2, and Y2). The procedure calculates the coordi-
nates of the midpoint and displays them in the message line.
330 Chapter 11

The declaration section of this procedure declares three instances of object


type point_t. The first two instances are also initialized using the default con-
structor of the object type and the values passed by the arguments. The body of the
trigger first compares the two points. If they are equal, the situation is trivial and
the message “The points are not distinct” is displayed to the user. Recall that two
objects are equal if and only if their attributes are mutually equal. If the points are
not equal, the method GetMidPoint on object A is invoked. The object B is passed
as an argument to this method and the object C is returned. This object contains the
coordinates of the midpoint which are displayed to the users in the message line.
As you can see, the familiar “dot” notation is used to access the attributes of an
object as well as to invoke any of the methods you have defined.

PROCEDURE MidPoints(X1 NUMBER, Y1 NUMBER, X2 NUMBER, Y2 NUMBER) IS


A point_t := point_t(X1, Y1);
B point_t := point_t(X2, Y2);
C point_t := point_t(NULL,NULL);
BEGIN
IF (A = B) THEN
MESSAGE('The points are not distinct.');
ELSE
C := A.GetMidPoint(B);
MESSAGE('MidPoint coordinates are: X=['||C.x||'],
Y=['||C.x||'].');
END IF;
END;

FIGURE 11.10 Using object instances in a PL/SQL program unit.

Let us revisit now the object type circle_t defined in Figure 11.3 earlier in
the chapter. This object contains an object of type point_t that describes the cen-
ter of the circle. Suppose now that you are developing an application that deals
with a large number of concentric circles, that is, circles that have a common point
on the plane as their center. There are two disadvantages with the current defini-
tion of circle_t. First, for each circle, you will create a point_t object instance
to hold the coordinates of the center. Obviously, you will consume memory loca-
tions to store identical object instances for these circles. Second, suppose your users
want to move the center of these circles to a new point on the plane. You applica-
tion will have to assign the new coordinates to the center objects of each circle.
It is clear that what you need here is a way to create only one instance for the
center point and refer to it from all the circles that need it. This way you will not
store multiple instances of the same object and you will have only one object to
maintain. In PL/SQL, references or pointers to objects are called refs. Figure 11.11
shows the revised definition of the object type circle_t in which the attribute
circle is a reference to an object of data type point_t.
Object-Relational Oracle8 and PL/SQL8 331

CREATE TYPE circle_t AS OBJECT (


centerREF point_t;
radiusNUMBER;
MEMBER FUNCTION GetArea RETURN NUMBER
);

FIGURE 11.11 Using REF modifiers in the object type definition.

For each object instance created, Oracle generates a universally unique identifi-
er, called the Object Identifier, or simply OID. Attributes or variables that reference
another object are assigned the OID of an object instance. The PL/SQL block shown
in Figure 11.12 declares one point_t object A, a reference to it A_ref, and two
circle_t objects C1 and C2. The first statement of the block stores the reference to
the object A in the variable A_ref. Then the point is initialized to the origin of coor-
dinates. After that, two circles are initialized. These circles share the same origin
pointed at by the OID of the center point. Their radii are 10 and 20 units, respectively.

DECLARE
A point_t := point_t(NULL, NULL);
A_ref REF point_t;
C1 circle_t := circle_t(NULL, NULL);
C2 circle_t := circle_t(NULL, NULL);
BEGIN
SELECT DEREF(A_ref) INTO A FROM DUAL;
A := point_t(0, 0);
C1 := circle_t(A_ref, 10);
C2 := circle_t(A_ref, 20);
END;

FIGURE 11.12 Assigning object references to REF attributes.

As the example shown in Figure 11.12 hints, the OID of an object is not directly
accessible. Similarly, the reverse operation, that is getting the actual object refer-
enced by the OID, is not yet available in PL/SQL. A workaround to both these
problems is to use the SQL operator DEREF. This operator must be used in a
SELECT statement and returns the value of the object in a predefined variable.
Typically, the SELECT statement is issued against the dummy table DUAL as in
Figure 11.12.
Figure 11.13 expands the PL/SQL block presented in Figure 11.12 in the form
of a procedure that takes as arguments the coordinates of two points on the plane
(X1, Y1, X2, and Y2). First, the procedure initializes the circles C1 and C2 to the
point (X1, Y1). Then, the center of the objects is moved to the point (X2, Y2). Finally,
the centers of the circles are dereferenced and their coordinates are printed in the
message console of the Developer/2000 Forms application.
332 Chapter 11

PROCEDURE MoveCircles(X1 NUMBER, Y1 NUMBER, X2 NUMBER,


Y2 NUMBER) IS
A point_t;
B point_t;
A_ref REF point_t;
C1 circle_t;
C2 circle_t;
BEGIN
SELECT DEREF(A_ref) INTO A FROM DUAL;
A := point_t(X1, Y1);
C1 := circle_t(A_ref, 10);
C2 := circle_t(A_ref, 20);
A := A.AddPoint(point_t(X2,Y2));
SELECT DEREF(C1.center) INTO B FROM DUAL;
MESSAGE('C1 new coordinates are:
X=['||B.x||'],Y=['||B.y||'].');
SELECT DEREF(C2.center) INTO B FROM DUAL;
MESSAGE('C2 new coordinates are:
X=['||B.x||'],Y=['||B.y||'].');
END;

FIGURE 11.13 Accessing referenced objects with the DEREF operator.

The examples used here have been very simple to allow you to focus on the
concepts rather than on implementation details. They are provided in the file
CH11_SQL of the companion CD-ROM. However, the real power of concepts such
as referencing and dereferencing is realized only when large and complex objects
are stored persistently in database structures. The following section covers details
on this topic and shows you how to insert and manipulate objects instances using
SQL statements.

11.3 Storing Objects in the Oracle8 Database

In the examples shown in the previous section, the object types you defined were
used as data types for attributes of other objects or as data types for PL/SQL vari-
ables. Oracle8 allows you to use these object types as data types for columns in a
table object. It also allows you to create tables that will store instances of a defined
data type. Figure 11.14 shows the definition of two object types, movie_t and
address_t, from the MRD application. Figure 11.15 shows three tables that use
these definitions. The table MOVIES_OBJ is an object table that stores instances of
movie_t. The table CUSTOMERS_OBJ is like the other tables you have seen so
far, except that one of its columns, ADDRESS, is of data type address_t. And
Object-Relational Oracle8 and PL/SQL8 333

finally, the table RENTALS_OBJ contains the column MOVIE_OID that references
a movie stored in the object table MOVIES. The statements that create these
objects and populate them with data are provided in file CH11_2.SQL on the com-
panion CD-ROM.

CREATE TYPE movie_t AS OBJECT (


id NUMBER,
title VARCHAR2(30),
director VARCHAR2(30),
actor VARCHAR2(30),
actress VARCHAR2(30),
rating VARCHAR2(8)
);

CREATE TYPE address_t AS OBJECT (


street VARCHAR2(30),
city VARCHAR2(30),
state VARCHAR2(2),
zip VARCHAR2(10)
);

FIGURE 11.14 Two object type definitions from the MRD application.

CREATE TABLE movies-obj( OF movie_t;

CREATE TABLE customers-obj (


id NUMBER,
last_name VARCHAR2(30),
first_name VARCHAR2(30),
phone VARCHAR2(14),
dob DATE,
member_dt DATE,
address address_t
);

CREATE TABLE rentals_obj( (


customer_id NUMBER,
movie_OID REF movie_t,
rent_dt DATE,
return_dt DATE,
daily_rate NUMBER(4, 2)
);

FIGURE 11.15 Database table objects that use object type definitions.
334 Chapter 11

11.3.1 SELECTING OBJECTS


When selecting objects from the database, you use their attributes in any of the
clauses of the SELECT statement. Two examples of such statements follow:

SELECT m.title, m.rating


FROM movies_obj m
WHERE m.actor = 'Anthony Hopkins';

SELECT c.address.zip, count(*)


FROM customers_obj c
WHERE c.address.state = 'VA'
GROUP BY c.address.ZIP;

There are two operators that are closely related to the use of objects in SELECT
statements. They are VALUE and REF. The first operator returns the value of an
object. The second operator returns the reference to a particular object instance.
Recall from the discussion about refs in Section 11.2.3 that the SQL operator
DEREF is used to return the object pointed to by a reference value. The statements
shown in Figure 11.16 use each of these operators. The first one uses VALUE to
select an object from the table MOVIES_OBJ with a given ID. The second one uses
REF to return the OID of the movie object retrieved by the first statement. And the
last statement returns the same object but this time by dereferencing the OID
returned by the second statement.

DECLARE
m_obj movie_t;
m_ref REF movie_t;
BEGIN
SELECT VALUE(m)
INTO m_obj
FROM movies_obj m
WHERE ID = '121';

SELECT REF(m)
INTO m_ref
FROM movies_obj m
WHERE ID = '121';

SELECT DEREF(m_ref)
INTO m_obj
FROM DUAL;
END;

FIGURE 11.16 Using the operators VALUE, REF, and DEREF.


Object-Relational Oracle8 and PL/SQL8 335

11.3.2 INSERTING OBJECTS


You can use the standard INSERT statement discussed in Chapters 9 and 10 to
insert records in object tables or in tables that contain object columns. The follow-
ing statements insert two records in the tables MOVIES_OBJ and
CUSTOMERS_OBJ, respectively:

INSERT INTO movies_obj


VALUES (123, 'Speed', NULL, NULL, 'Sandra Bullock', 'PG-13');

INSERT INTO customers_obj


VALUES (21, 'Johnson', 'Michelle', NULL, NULL, NULL,
address_t('123 Commerce Street', 'Springfield', 'VA',
'22152'));

As you can see from the second statement, the default constructor method
provided by Oracle must be used in order to insert data in a columns that contains
object instances. If the table is a table of objects, the constructor is not required, as
the first example shows.
When inserting object references, you may find the RETURNING clause of
the INSERT statement very handy. This clause returns the reference to the newly
inserted object to a PL/SQL variable. This reference can then be used for further
processing without the need to query the database for it. Figure 11.17 shows a
PL/SQL anonymous block that inserts an object in the table MOVIES and then
records a rental transaction for the new object in the table RENTALS.

DECLARE
movie_ref REF movie_t;
BEGIN
INSERT INTO movies_obj m
VALUES (movie_t(123, 'Speed', NULL, NULL, 'Sandra Bullock',
'PG-13'))
RETURNING REF(m) INTO movie_ref;

INSERT INTO RENTALS_obj


VALUES (21, movie_ref, '02-SEP-98', NULL, 0.99);
END;

FIGURE 11.17 Inserting object references using the RETURNING clause.

You can also insert objects in an object table that are returned by a query.
Assuming that a table called R_MOVIES has been created, the following statement
inserts in this table all the movies rated “R” from the object table MOVIES_OBJ.
336 Chapter 11

Notice in this statement the operator VALUE which returns the value of the objects
retrieved by the subquery. Alternatively, you could have used the familiar
SELECT * form of the subquery.

INSERT INTO R_movies


SELECT VALUE(m) FROM MOVIES_OBJ m
WHERE m.rating = 'R';

11.3.3 UPDATING OBJECTS


As with the INSERT statement, you can use the traditional version of the UPDATE
statement or one that uses the constructor of an object type. The following state-
ments update the ZIP of the customer with ID 21. Notice that when you use the
constructor method, you need to provide the correct values for all the attributes of
the object and not only the ones that you want to modify.

UPDATE customers_obj c
SET c.address.zip = '22153'
WHERE c.ID = 21;

UPDATE customers_obj
SET address = address_t('123 Commerce Street',
'Springfield', 'VA', '22153')
WHERE ID = 21;

11.3.4 DELETING OBJECTS


The process of deleting objects from the database is very simple. Only two state-
ments are provided here for you to inspect:

DELETE FROM customers_obj c


WHERE c.address.state = 'VA';

DELETE FROM movies_obj


WHERE movies_obj.actress = 'Sandra Bullock';

Notice however that the table RENTALS_OBJ may contain records which ref-
erence the movie you just deleted in the second statement above. The record insert-
ed in the statements of Figure 11.17 is one example. For object references, Oracle
does not enforce the same referential integrity mechanism similar to the foreign
key constraints defined in the relational model. Instead, it leaves the reference dan-
gling. Since, dangling references always cause errors and unexpected exceptions
at runtime, you should be careful to nullify all the references to an object that you
Object-Relational Oracle8 and PL/SQL8 337

delete. Thus, the following UPDATE statement should append the DELETE state-
ment shown above:

UPDATE rentals_obj
SET movie_OID = NULL
WHERE movie_OID IS DANGLING;

11.4 Collections

Chapter 10 discussed index-by tables as a data type that allows you to introduce
array-like structures in you PL/SQL program units. Index-by tables were intro-
duced in version 2 of PL/SQL that came along with Oracle7. Oracle8 expands your
ability to define customized tabular structures by introducing nested tables and
variable-size arrays (VARRAYs). The features of these collection types will be the
subject of this section. In the remainder of this section the term varray will denote
an object of type VARRAY.

11.4.1 OVERVIEW OF COLLECTIONS


Nested tables and varrays are PL/SQL structures that resemble one-dimensional
arrays in the sense that they organize objects of the same data type based on the val-
ues of a subscript. These values always begin at 1, unlike array structures in other
languages in which the subscript of the first element is 0. Using the subscript, you can
access an element in these structures using the subscript. Nested tables and varrays
have a number of similarities with objects in Oracle8. Some of them are listed here.

❏ You need to define a collection (nested table or varray) type before initializ-
ing any instances of these collections. However, while object types can be
defined only using DDL statements and are stored as part of the schema, col-
lection types may be defined as part of the schema as well as in any PL/SQL
program unit. Figure 11.18 shows the DDL statements to create a varray and
nested table type in the Oracle8 schema. Figure 11.19 shows how you can
define the same types in the DECLARE section of a PL/SQL block.
❏ Oracle8 allows you to initialize a collection using a constructor method sim-
ilar to the one used to initialize object instances. Figure 11.19 shows examples
of how you declare two collection variables and initialize them.
❏ Collection types may be data types of PL/SQL variables as well as object
attributes and table columns. Figure 11.18 shows a revised definition of the
object movie_t introduced in Figure 11.14. In the new object type movies_t
338 Chapter 11

the attributes DIRECTOR, ACTOR, and ACTRESS are of the varray data type
people_t. This allows you to store up to ten names in each of these
attributes rather than just one as in the previous definition.

CREATE TYPE people_t AS VARRAY(10) OF VARCHAR2(30);

CREATE TYPE rental_t AS OBJECT (


customer_id NUMBER,
rent_dt DATE,
return_dt DATE,
daily_rate NUMBER(4, 2)
);

CREATE TYPE rental_tab_t AS TABLE OF rental_t;

CREATE TYPE movies_t AS OBJECT (


id NUMBER,
title VARCHAR2(30),
director people_t,
actor people_t,
actress people_t,
rating VARCHAR2(8)
);

FIGURE 11.18 Defining nested table and varray types with DDL scripts.

DECLARE
TYPE people_t IS VARRAY(10) OF VARCHAR2(30);
TYPE rental_tab_t IS TABLE OF rental_t;

actors people_t := people_t('Anthony Hopkins', 'Tom Hanks',


'Robin Williams', 'Gene Hackman');
Piano_rentals rental_tab_t;
BEGIN
SELECT customer_id, rent_dt, return_dt, daily_fee
INTO Piano_rentals
FROM rental
WHERE movie_id = '121';
-- Other statements may go here.
END;

FIGURE 11.19 Declaring and initializing collection instances in a PL/SQL block.


Object-Relational Oracle8 and PL/SQL8 339

As you will see in the following section, the similarities between nested tables
and varrays extend beyond the ones listed above. They include the way you work
with them and the methods defined for each collection type. However, there are
also important differences between nested tables and varrays. You need to be
aware of them in order to select the appropriate collection in a given situation. The
following is a list of these differences.

❏ For nested tables, the upper bound of the subscript is practically unlimited
(232 - 1) whereas the upper bound of varrays is fixed at the time of the type
declaration.
❏ Varrays are always compact, with no gaps between their elements, whereas
nested tables, like index-by tables, allow you to delete elements from them
without compacting the entire structure.
❏ When stored in database structures, varray data are located in the same data-
base block as the data from the other fields of the table record. The nested
table data on the other hand are stored in a separate table (hence the term
nested table) whose records reside in separate data blocks and may be locat-
ed in a different tablespace.
❏ The varray elements are maintained as a block at the varray level. This
means that in order to select, insert, update, or delete an element in the
varray, you have to perform the operation on the entire structure, carrying
with you all the other elements as well. Nested tables on the other hand offer
more flexibility and granularity. They allow you to access and manipulate
individual records within the table just as you would do with records in a
regular Oracle table.

Finally, the following are a few differences between nested tables and index-
by tables:

❏ The subscript of nested tables is a positive number ranging from 1 to (232 - 1),
whereas the subscript of index-by tables may be anywhere in the range -(232 - 1)
to (232 - 1), including negative integers.
❏ Index-by tables are intended to be used in PL/SQL program units and cannot
be manipulated with SQL statements. In nested tables, on the other hand,
you can use the SQL commands, like SELECT, INSERT, UPDATE, and
DELETE, as you would do with a standard Oracle table.
❏ There is a list of data types that elements of nested tables cannot have but are
allowed for index-by tables. These include BOOLEAN, LONG, LONG RAW,
and BINARY_INTEGER.
❏ Index-by tables are unbounded in the sense that you can store elements in
them anywhere in the range of their subscript. Nested tables have a lower
340 Chapter 11

bound because their subscript cannot be smaller than 1. Although they have
no upper bound, you need to extend them until they include or surpass the
desired value of the subscript before storing an element in that position. For
example, to store the first element in a nested table, you need to extend it at
least by one and then store the element in the first position in the table.
❏ Typically, nested tables are compact initially and may become sparse as you
delete elements from them. Index-by tables on the other hand are sparse to
begin with and may be filled in certain regions with elements.

11.4.2 WORKING WITH COLLECTIONS


This section presents examples of the major operations you perform with collec-
tion objects. The objects that will be used in this section are shown in Figure
11.20. The statements to create these objects are provided in the file CH11_3.SQL
on the companion CD-ROM. You are already familiar with the varray people_t
and nested table rental_tab_t defined in the previous section. In addition,
Figure 11.20 shows the structure of the table MOVIES_TABLE which will store
the values of the columns DIRECTOR, ACTOR, and ACTRESS as in-line varrays
and all the rental transactions for a given movie in the out-of-line table
RENTALS_TABLE.

CREATE TYPE people_t AS VARRAY(10) OF VARCHAR2(30);

CREATE TYPE rental_t AS OBJECT (


customer_id NUMBER,
rent_dt DATE,
return_dt DATE,
daily_rate NUMBER(4, 2)
);

CREATE TYPE rental_tab_t AS TABLE OF rental_t;

CREATE TABLE movies_table (


id NUMBER,
title VARCHAR2(30),
director people_t,
actor people_t,
actress people_t,
rating VARCHAR2(8),
rentals rental_tab_t)
NESTED TABLE rentals STORE AS rentals_table;

FIGURE 11.20 Definitions of collection types and of a table object.


Object-Relational Oracle8 and PL/SQL8 341

Two situations may arise when you work with collections stored in the data-
base. The first one is when you consider them as “black-boxes” and manipulate
them like any other data fields in the table record. Figure 11.21 shows a PL/SQL
block with operations from this scenario. In the statements shown in this figure,
you insert a movie in the database, update its ACTRESS column, and then select
the new record from the database.

DECLARE
actorspeople_t := people_t('Tom Cruise', 'Ed Harris');
actressespeople_t := people_t('Holly Hunter',
'Jeanne Tripplehorn');
rental_trx rental_tab_t := rental_tab_t(
rental_t(212, '02-SEP-98', NULL, 0.99),
rental_t(213, '03-SEP-98', NULL, 0.99),
rental_t(541, '06-SEP-98', NULL, 1.49));
movie_idNUMBER;
BEGIN
INSERT INTO movies_table (id, title, actor, rentals)
VALUES(189, 'The Firm', actors, rental_trx);

UPDATE movies_table SET actress = actresses WHERE id = 189;

SELECT id, actor, actress, rentals


INTO movie_id, actors, actresses, rental_trx
FROM movies_table
WHERE id = 189;
END;

FIGURE 11.21 Working with collection objects at the object level.

The most interesting situation is the one in which you want to access the col-
lection at a more granular level and work with its individual elements. For exam-
ple, each time a new rental transaction occurs, you will want to insert a new record
in the nested table stored in the column RENTALS of the movie in question. When
the movie is returned, you want to update the transaction record with the return
date; you may also want to see the number of all the transactions generated for a
movie on a given date; and finally you may want to delete a transaction from the
nested table. Figure 11.22 shows a PL/SQL block with statements that manipulate
the records in the nested table along these lines.
Notice that these statements differ from the regular statements you issue
against an Oracle table only by the way you define the table name. Normally in
clauses like INSERT INTO <table>, UPDATE <table>, SELECT … FROM
<table>, or DELETE <table>, you specify the name of a given table from the
schema. When the statements are issued against a nested table, you need to use the
342 Chapter 11

operand THE to specify the table object. This operand works with a subquery
which must return exactly one nested table object. In the examples shown in Figure
11.22, this subquery returns the nested table stored in the column RENTALS of the
movie record with ID 189.

DECLARE
trx_countNUMBER;
BEGIN
INSERT INTO THE ( SELECT rentals FROM movies_table WHERE
id=189)
VALUES ( rental_t(543, '07-SEP-98', NULL, 1.49));

UPDATE THE ( SELECT rentals FROM movies_table WHERE id=189)


SET return_dt = '09-SEP-98'
WHERE customer_id = 543 and return_dt IS NULL;

SELECT COUNT(*)
INTO trx_count
FROM THE ( SELECT rentals FROM movies_table WHERE id=189)
WHERE rent_dt = '07-SEP-98';

DELETE THE ( SELECT rentals FROM movies_table WHERE id=189)


WHERE rent_dt > '01-JAN-90';
END;

FIGURE 11.22 Accessing elements of nested tables.

When the differences between nested tables and varrays were listed in the pre-
vious section, it was mentioned that nested tables allow you to manipulate their
elements individually but varrays do not. In the examples shown so far in this sec-
tion, you have seen how easy it is to select, insert, update, or delete a single element
from nested tables. The programming is more involved if you want to perform
similar tasks with varray columns. The following is a list of steps you need to fol-
low to accomplish these tasks:

1. Retrieve the varray object into a PL/SQL variable of the same type. If you
intend to modify the value of any elements within the object, place a database
lock on the record.
2. If you are planning to insert a new element, extend the collection.
3. Loop through all the varray elements until you find the element you want to
select, update, or delete, or until you find the place where you want to insert
the new element.
4. Perform the desired operation with the selected element.
5. Update the table with the new value of the varray object.
Object-Relational Oracle8 and PL/SQL8 343

The PL/SQL block shown in Figure 11.23 shows how you could follow these
steps in order to add an actor to the list of actors in the movie “The Firm” created
in Figure 11.21.

DECLARE
actors people_t;
j INTEGER;
BEGIN
SELECT actor INTO actors FROM movies_table WHERE id = 158
FOR UPDATE OF actor;

actors.EXTEND(2);
j := actors.LAST - 1;
actors(j) := 'Gene Hackman';
actors(j + 1) := 'Hal Holbrook';

UPDATE movies_table SET actor = actors WHERE id = 158;


END;

FIGURE 11.23 Accessing elements of varray objects.

The Objects Option of Oracle8 allows you to define abstract data types that encap-
sulate data attributes with operations on those attributes. It also extends the previ-
ous support for collections by introducing nested tables and variable-size arrays.
This chapter highlights the major features of objects and collections. Its main topics
included:

❏ Overview of the Object-Relational Technology

❏ Object Types and Object Instances


❏ Defining Object Types
❏ Attributes and Methods
❏ Working with Object Instances

❏ Storing Objects in the Oracle8 Database


❏ Selecting Objects
❏ Inserting Objects
❏ Updating Objects
❏ Deleting Objects

❏ Collections
❏ Overview of Collections
❏ Working with Collections
344 Chapter 11

The following table describes the software assets provided in the companion
CD-ROM that were discussed in this chapter. From the main page of the CD-ROM
follow the links Software and Chapter 11 to access these assets:

Asset Name Description

SQL files with the statements that create the


CH11_1.SQL objects discussed in Section 11.2 of the
chapter.

SQL files with the statements that create the


CH11_2.SQL objects discussed in Section 11.3 of the
chapter.

SQL files with the statements that create the


CH11_3.SQL objects discussed in Section 11.4 of the
chapter.