CHAPTER 8 • ORACLE OBJECT-ORIENTED FEATURES 352 Another option for an object table is to define the primary key of the table to be the OID, using the OID clause of the CREATE TABLE statement. If you use the pri- mary key as the OID for the table, it will only be unique to the table itself. This is an alternative to using the default system-generated OID (a 128 byte, base 64 number), which is globally unique throughout the database. If you specify that the table should use the system-generated OID, then you will want to specify the STORAGE clauses for the related OID index that will be created. Thus, using the system-generated OID will require more storage space than using the primary key. Listing 8.9 provides an example of the creation of both the child and the parent object tables, using a few of the options just described. Listing 8.9: Creating Object Tables Drop all types and tables to make this easier. DROP TABLE child; DROP TABLE parent; DROP TYPE parent_type FORCE; DROP TYPE child_type FORCE; CREATE OR REPLACE TYPE name_type AS OBJECT( First_name VARCHAR2(30), Last_name VARCHAR2(30), Middle_init CHAR(1) ) / Create our forward type declaration for the child_type type so we can create the parent_type type. CREATE OR REPLACE TYPE child_type / Create the parent_type type. Note the two REFs to the child_type. CREATE OR REPLACE TYPE parent_type AS OBJECT ( Parent_id NUMBER, Parent_name name_type, Parent_address address_type, Child_name REF child_type, Exch_student REF child_type) / C opyright ©2002 SYBEX, Inc., Alameda, CA www.sybex.com Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 353 Now, create the child_type. Again, note the REFs to the parent_type type. CREATE OR REPLACE TYPE child_type AS OBJECT ( Parent_id REF parent_type, Child_id NUMBER, Child_name name_type, Child_address address_type, Teacher REF parent_type) / Now, create the parent table. We will use an OID index for this table. This is generally required for the main table in a parent/child relationship. The OIDINDEX clause will help speed up REF lookups between this and the child table. CREATE TABLE parent OF parent_type OIDINDEX idx_parent (TABLESPACE indexes) TABLESPACE users PCTFREE 10 PCTUSED 70 STORAGE (INITIAL 100k NEXT 100k PCTINCREASE 0); Now, we are going to add a primary key constraint to the parent table. ALTER TABLE parent ADD CONSTRAINT pk_parent PRIMARY KEY (parent_id) USING INDEX PCTFREE 10 TABLESPACE indexes STORAGE (INITIAL 10k NEXT 10k); CREATE TABLE child OF child_type (parent_id WITH ROWID SCOPE IS parent, teacher WITH ROWID SCOPE IS parent) OIDINDEX oid_child (TABLESPACE indexes) TABLESPACE users STORAGE (INITIAL 100k NEXT 100k PCTINCREASE 0); USING OBJECT TABLES Oracle Database Administration PART II C opyright ©2002 SYBEX, Inc., Alameda, CA www.sybex.com Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. CHAPTER 8 • ORACLE OBJECT-ORIENTED FEATURES 354 There is a lot going on in Listing 8.9, but much of it is building on what you have already learned (and we will build even more on this example shortly). Let’s dissect what this code is doing. First, we drop the types and tables that we will be creating. This just makes the overall re-creation process much easier. Next, we create a forward type declaration for the CHILD_TYPE type, since we will need it to create the PARENT_TYPE type. Next, we create the parent type and then the child type. As before, each type has REFs (ref- erences) to each other (yes, we know, true evil nastiness). Then we begin to create the object tables. The first thing you may be wondering about is the OIDINDEX clause that you see in both CREATE TABLE statements. The OIDINDEX clause will create an index on the OID of the tables. This is not unlike a regular index on a primary key. The OID index can significantly speed up REF queries between object tables. Since both tables will be referencing each other, both indexes will be helpful. If you had only one table with REFs, then you definitely would want an OID index on the table being referenced. In this case, an OID index on the other table—the one that has no references to it—would not be needed. The parent table creation is a fairly straightforward operation that you have already seen in earlier examples. The child table creation is a bit different. Here, you see some new syntax in the form of: (parent_id WITH ROWID SCOPE IS parent, teacher WITH ROWID SCOPE IS parent) What we are doing here is completing what we started. When we defined the PARENT_ID and TEACHER_ID attributes of the PARENT_TYPE type (in the CREATE TYPE statement), we used the REF clause to indicate that the PARENT_ID and TEACHER_ID attributes would reference row objects in some other object table. I’m sure that you were wondering just what that REF was going to be used for. Well, here is your answer. When we first created the REF, we didn’t have an actual object to refer- ence it to. Remember that types are just templates, and beyond that, they do not store anything. Thus, when you create a type, you don’t need anything to reference an attribute to. Now that we have created the object tables (and thus allocated some stor- age to an instance of the PARENT_TYPE), we can define what object these REFs are referring to. In Listing 8.9, we have two different operations going on. This is known as scoping the REF. Also notice in Listing 8.9 that we are storing the ROWID pseudocolumn with the OID of each row. Like scoping the REF to a specific table, this can speed up certain query activities. Neither of these actions is required, but taking them will reduce space requirements and can speed up access to the table being referenced. C opyright ©2002 SYBEX, Inc., Alameda, CA www.sybex.com Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 355 Have you noticed that something is still missing? Think about it for a moment and see if you can figure out what it is. There is something we still have not done yet, and that leads us to the topic of altering object tables. Altering Object Tables After you create an object table, you may need to later alter the nature of that table. As you might guess, the ALTER TABLE statement is what you are looking for. So, going back to the previous section, did you figure out what it was we still needed to do? What we have not done yet is to scope out the REFs for the parent table. This is because when we created the parent object table, we had not yet created the child table. After creating the child table, we can scope the circular references that are in the parent table, as well as set up the ROWID storage. We execute these opera- tions in the last two ALTER TABLE statements, shown in Listing 8.10. Listing 8.10: Altering Object Tables This enables our circular reference from parent to child for the parent table. This is the same as the “parent_id WITH ROWID SCOPE IS parent” line in the CREATE TABLE statement for the child table. Since the child table was not present when we created the parent table, we could not do this yet. Now that the child table has been created, we can create the scoped REF. ALTER TABLE parent ADD (REF(child_name) WITH ROWID, REF(exch_student) WITH ROWID) / ALTER TABLE parent ADD (SCOPE FOR (child_name) IS CHILD, SCOPE FOR (exch_student) IS CHILD) / As you would expect, you can also use the ALTER TABLE statement to change a number of characteristics of an object table, just as you would a relational table. This includes STORAGE clauses, partitioning, and so on. NOTE One big difference between object tables and relational tables is that it is not possible to add a column to an object table. This is because an object table is wholly defined by a user-defined type. USING OBJECT TABLES Oracle Database Administration PART II C opyright ©2002 SYBEX, Inc., Alameda, CA www.sybex.com Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. CHAPTER 8 • ORACLE OBJECT-ORIENTED FEATURES 356 Dropping Object Tables Well, it doesn’t get much simpler than this. You use the DROP TABLE clause to drop an object table: DROP TABLE parent; As you might expect, there are a couple of nasty potential results from dropping an object table. If you drop a parent table and leave a child table (or even vice-versa, as in our example), you can end up with dangling REFs—the orphaned table will have ref- erences to rows in a table that no longer exists. This can also occur if you drop parent rows but do not clean up the child records. This is bad news, but Oracle has provided a way to deal with this problem through the use of the ANALYZE command, using the VALIDATE REF UPDATE SET DANGLING TO NULL option. As explained in the “Intro- ducing Row and Column Objects, OIDs, and REFs” section earlier in this chapter, this option will update all ROWIDs for REFs, and set dangling REFs to NULL. Getting Information about Object Tables There are a couple of data dictionary views that are useful for managing object tables. These include the DBA_OBJECT_TABLES view and the DBA_REFS data dictionary views. The DBA_OBJECT_TABLES View Do you want to see a listing of object tables? You won’t find them in the DBA_TABLES view. You’ll need to use the DBA_OBJECT_TABLES view, which provides information about the object table, such as the owner name, object table name, the tablespace name that the object table is assigned to, as well as the results of the ANALYZE process executed against that object table. Here is an example of a query against the DBA_OBJECT_TABLES view and its results: SELECT owner, table_name, tablespace_name, initial_extent, next_extent FROM dba_object_tables WHERE owner=’SCOTT’; OWNER TABLE_NAME TABLESPACE_NAME INITIAL_EXTENT NEXT_EXTENT SCOTT CHILD USERS 102400 102400 SCOTT PARENT USERS 102400 102400 C opyright ©2002 SYBEX, Inc., Alameda, CA www.sybex.com Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 357 NOTE Even though you won’t find object tables in the DBA_TABLES view, you will find the columns of an object table in the DBA_TAB_COLUMNS view. The DBA_REFS View The DBA_REFS data dictionary view describes the REFs present in an attribute of an object type. This view describes the owner and object that contains the REF, as well as other information about the REF. Here is an example of a query against this data dic- tionary view and its results: SELECT owner, table_name, column_name,is_scoped, scope_table_owner, scope_table_name FROM dba_refs WHERE owner NOT LIKE ‘%SYS%’; OWNER TABLE_NAME COLUMN_NAME IS_ SCOPE_TABL SCOPE_TABLE_NA SCOTT CHILD PARENT_ID YES SCOTT PARENT SCOTT CHILD TEACHER YES SCOTT PARENT SCOTT PARENT CHILD_NAME NO SCOTT PARENT EXCH_STUDENT NO Using Collection Types If you have had any programming experience, you know what an array is. A collec- tion is not unlike an array. A collection allows you to store one or more object types in a given row. In other words, with a collection type, you can denormalize a table, stor- ing multiple related objects of information in a given row that is related to that infor- mation. The two collection types in Oracle are VARRAYs and nested tables. Each has its own distinguishing characteristics, but they are similar in nature. Working with VARRAYs A VARRAY (or varying array) is much like an array in a programming language such as C or BASIC. A VARRAY column allows you to store multiple values in the same allo- cated datatype. You can define a VARRAY as an attribute of a table or as a volatile ele- ment in a PL/SQL routine. USING COLLECTION TYPES Oracle Database Administration PART II C opyright ©2002 SYBEX, Inc., Alameda, CA www.sybex.com Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. CHAPTER 8 • ORACLE OBJECT-ORIENTED FEATURES 358 A VARRAY is an ordered set of data elements of the same type. Each element of a VARRAY contains an index. An index is a number that points to the order of the data element in the array. Because you need to define the boundaries of the array (or how many records it can store), the VARRAY is somewhat more limiting than its cousin, the nested table. Once created, the boundaries of the array cannot be redefined with- out dropping and re-creating the VARRAY object. A VARRAY cannot contain another collection type in its definition. Also, VARRAY types cannot store LOBs, while nested tables can. Finally, a VARRAY has a storage limit of 2GB. These restrictions obviously limit the effectiveness of the VARRAY collection type. Just as you can store LOBs out of line in a given object to improve performance, you can also store VARRAYs out of line with the rest of the data in the object table. The associated LOB storage segment must be contained in the same tablespace as the object table. When you define the object that contains the VARRAY, you can force Oracle to store the data out of line with the DISABLE STORAGE IN ROW clause of the CREATE TABLE command. The elements of a VARRAY must be packed, meaning that you need to start with index 0 for the first record, store the second in index 1, and so on. You cannot store record one in position two and record three in position four. This also implies that you cannot remove records from a VARRAY except from the uppermost used index. Creating VARRAYs To create a VARRAY, use the CREATE TYPE command, just as you would to create a user type. When creating a VARRAY, you must provide a type name for that array. The type name can be a built-in datatype (such as NUMBER or VARCHAR2), a REF, or an object type (such as ADDRESS_TYPE). Listing 8.11 provides an example of creating a VARRAY. Listing 8.11: Creating a VARRAY DROP TABLE parent; DROP TYPE address_type FORCE; DROP TYPE name_type FORCE; DROP TYPE parent_type FORCE; First, create the address_type type. CREATE OR REPLACE TYPE address_type AS OBJECT ( Address_number NUMBER, Street_address_one VARCHAR2(50), Street_address_two VARCHAR2(50), City VARCHAR2(30), C opyright ©2002 SYBEX, Inc., Alameda, CA www.sybex.com Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 359 State VARCHAR2(2), Zip_code VARCHAR2(10) ) / CREATE OR REPLACE TYPE address_varray AS VARRAY(5) OF address_type / Create the name_type type. CREATE TYPE name_type AS OBJECT( First_name VARCHAR2(30), Last_name VARCHAR2(30), Middle_init CHAR(1) ) / Create the parent_type type. CREATE TYPE parent_type AS OBJECT ( Parent_id NUMBER, Parent_name name_type, Parent_address address_varray ) / Create the object table with the VARRAY. CREATE TABLE parent OF parent_type ( PRIMARY KEY (parent_id) USING INDEX TABLESPACE indexes STORAGE (INITIAL 10k NEXT 10k PCTINCREASE 0) ) OBJECT ID PRIMARY KEY VARRAY parent_address STORE AS LOB parent_address_varray (DISABLE STORAGE IN ROW ) PCTFREE 10 PCTUSED 70 STORAGE (INITIAL 100k NEXT 100k PCTINCREASE 0); In this example, we begin by dropping the table and types that we will be creating. This is so we get a fresh start with each creation. Next, we create the ADDRESS_TYPE type. Following that is the creation of the VARRAY type. Notice that when we create the VARRAY type, we are, again, just creating a type. There is still no storage associ- ated with this type; it is just another type defined in the data dictionary. We proceed to create the other types (NAME_TYPE and PARENT_TYPE) that we will need in the object table to be created in this example. USING COLLECTION TYPES Oracle Database Administration PART II C opyright ©2002 SYBEX, Inc., Alameda, CA www.sybex.com Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. CHAPTER 8 • ORACLE OBJECT-ORIENTED FEATURES 360 Now comes the fun part—the actual creation of the object table that we call PAR- ENT. The creation syntax is straightforward. First, we indicate that the parent object table will be of the type PARENT_TYPE. Next, we define the primary key for the object table to be the PARENT_ID column, and we use the USING INDEX clause to define the tablespace and storage characteristics of the primary key index, just as we would with a normal relational table. Next, we define the OID to be the primary key of the table. (Recall that the OID is a unique identifier for each row in the table.) Following the definition of the primary key as the OID, we define the VARRAY that will be contained in this table, starting with this command: VARRAY parent_address STORE AS LOB parent_address_varray; The VARRAY is the attribute PARENT_ADDRESS in the type PARENT_TYPE. We are storing the VARRAY data as a LOB datatype, and the LOB segment that will be created is PARENT_ADDRESS_VARRAY. Note the use of the DISABLE STORAGE IN ROW clause, which is optional. This forces Oracle to store all of the data associated with the VARRAY out of line, in the LOB segment. If the DISABLE STORAGE IN ROW clause is not used, or the default ENABLE STORAGE IN ROW clause is used, then the first 4000 bytes of the data (more or less, depending on how finicky Oracle is feeling on a given day) will be stored in line. The remaining data will be stored out of line in the LOB segment. Note that you can also create a VARRAY as a PL/SQL variable, as shown in this example: CREATE OR REPLACE TYPE number_varray AS VARRAY(10) OF NUMBER; Altering and Dropping VARRAYs There isn’t really anything to alter with regard to a VARRAY. If you need to make changes to the definition of a VARRAY, you will need to drop the type and re-create it. This implies that you will need to preserve your data before you make the change. If you created a VARRAY and then decide that it just isn’t working out, you can drop the thing (in Nevada, there is no waiting period required). To drop a VARRAY, simply use the DROP TYPE command. You can use the ALTER TABLE statement to modify some of the attributes of the actual VARRAY assigned to the table. This would include the LOB STORAGE clause (you can change the PCTVERSION and CHUNK parameters, for example). Here is an example of changing the PCTVERSION: ALTER TABLE parent MODIFY VARRAY parent_address (PCTVERSION 20); C opyright ©2002 SYBEX, Inc., Alameda, CA www.sybex.com Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 361 Note that once you have created or added a VARRAY, you cannot change the set- ting for storing rows in line. Working with Nested Tables The second cousin (once removed) to the VARRAY is the nested table. The nested table type has the same job as the VARRAY, which is to store repeating data for a sin- gle row. The primary differences between a nested table and a VARRAY are that a nested table is not bounded and that the elements of a nested table can be sparsely populated. If you have a nested table with three elements, you can remove the middle element without affecting the other elements. The data in a nested table is stored out of line in an object that you define when you create the table with the nested table. The table created to store the nested table data is stored in the same tablespace as the table that the data is associated with, and this default cannot be changed. Creating Nested Tables When you create a nested table, you define a primary key index for the nested object, just as you would for any other table. You can also define the storage of the nested table as a hash table, which is the default, or as an index-organized table. As with VARRAYs, nested tables cannot contain other collection types; thus, a nested table type cannot contain a reference to another nested table. Listing 8.12 shows an example of creating a nested table. This example makes the ADDRESS_TYPE type a nested table in the PARENT table (assume our parents have multiple homes). Listing 8.12: Creating a Nested Table DROP TYPE address_type FORCE; DROP TYPE address_type_nt FORCE; DROP TYPE name_type FORCE; DROP TYPE parent_type FORCE; DROP TABLE parent; First, create the address_type type. CREATE OR REPLACE TYPE address_type AS OBJECT ( Address_number NUMBER, Street_address_one VARCHAR2(50), Street_address_two VARCHAR2(50), City VARCHAR2(30), USING COLLECTION TYPES Oracle Database Administration PART II C opyright ©2002 SYBEX, Inc., Alameda, CA www.sybex.com Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.