INFORMIX
Informix Guide to SQL: Tutorial
Chapter 10: Understanding Complex Data Types
Home Contents Index Master Index New Book

What Are Complex Data Types?

A complex data type is a user-defined data type that can contain multiple data types of any kind and in any combination. An important characteristic of a complex data type is that you can easily access each of its component data types. In contrast, built-in types and opaque types are self-contained (encapsulated) data types. Consequently, the only way to access the component values of an opaque data type is through functions that you define on the opaque type. (For more information on opaque data types, see Chapter 3, "Environment Variables," in the Informix Guide to SQL: Reference.)

Figure 10-1 shows the complex types that Universal Server supports and the syntax that you use to create the complex types.

Figure 10-1
Complex Types

The complex types illustrated in Figure 10-1 provide the following extended data type support:

You can use complex types in the same way that you use built-in or opaque data types. For example, you can use complex types as:

For complete information about how to perform SELECT, INSERT, UPDATE, and DELETE operations on the complex data types described in this chapter, see Chapter 12, "Accessing Complex Data Types."

Named Row Types

A named row type is a group of fields that are defined under a single name. A field refers to a component of a row type and should not be confused with a column, which is associated with tables only. The fields of a named row type are analogous to the fields of a C-language structure or members of a class in object-oriented programming. Once you create a named row type, the name that you assign to the row type represents a unique type within the database. To create a named row type, you specify a name for the row type and the names and data types of its constituent fields. The following example shows how you might create a named row type called person_t:

The person_t row type contains six fields: name, address, city, state, zip, and bdate. You can use any data type to define the fields of a row type, except the TEXT, BYTE, SERIAL, or SERIAL8 data type. When you create a named row type, you can use it just as you would any other data type. For example, person_t can occur anywhere that you might use any other data type.

For the syntax you use to create a named row type, see the CREATE ROW TYPE statement in the Informix Guide to SQL: Syntax. For information about how to cast row type values, see Chapter 13 in this manual.

When to Use a Named Row Type

A named row type is one way to create a new data type in Universal Server. When you create a named row type, you are defining a template for fields of data types known to the database server. Thus the field definitions of a row type are analogous to the column definitions of a table: both are constructed from data types known to the database server.

You can create a named row type when you want a type that acts as container for component values that users need to access. For example, you might create a named row type to support address values since users need direct access to the individual component values of an address such as street, city, state, and zip code. When you create the address type as a named row type, users always have direct access to each of the fields.

In contrast, if you create an opaque data type to handle address values, a C-language data structure stores all the address information. Because the component values of an opaque type are encapsulated, you would have to define functions to extract the component values for street, city, state, zip code. Thus, an opaque data type is a more complicated type to define and use.

Before you define a data type, determine whether the type is just a container for a group of values that users can access directly. If the type fits this description, use a named row type.

Choosing a Name for a Named Row Type

You can give a named row type any name that you like provided that the name does not violate the conventions established for the SQL identifiers. The conventions for SQL identifiers are described in the Identifier segment in the Informix Guide to SQL: Syntax. To avoid confusing type and table names, the examples in this manual designate named row types with the _t characters at the end of the row type name.

You must have the Resource privilege to create a named row type. The name that you assign to a named row type should not be the same as any other data type that exists in the database because all data types share the same name space. In an ANSI-compliant database, the combination owner.type must be unique within the database. In a database that is not ANSI-compliant, the name must be unique within the database.

Important: You must grant USAGE privileges on a named row type before other users can use it. For information about granting and revoking privileges on named row types, see Chapter 11, "Granting and Limiting Access to Your Database."

Restrictions on Named Row Types

You cannot use the following data types to define fields of a named row type:

Informix recommends that you use the BLOB or CLOB data types instead of the TEXT or BYTE data types when you create a typed table that contains columns for large objects. For backward compatibility, you can create a named row type that contains TEXT or BYTE fields and use that type to recreate an existing (untyped) table as a typed table. However, although you can use a row type that contains BYTE or TEXT fields to create a typed table, you cannot use such a row type as a column. You can use a row type that contains CLOB or BLOB fields in both typed tables and columns.

In a CREATE ROW TYPE statement, you can specify only the NOT NULL constraint for the fields of a named row type. You must define all other constraints in the CREATE TABLE statement. For more information, see the CREATE TABLE statement in the Informix Guide to SQL: Syntax.

Using a Named Row Type to Create a Typed Table

You can create a table that is typed or untyped. A typed table is a table that has a named row type assigned to it. An untyped table is a table that does not have a named row type assigned to it. The CREATE ROW TYPE statement creates a named row type but does not allocate storage for instances of the row type. To allocate storage for instances of a named row type, you must assign the row type to a table. The following example shows how to create a typed table:

The first statement creates the person_t type. The second statement creates the person table, which contains instances of the person_t type. More specifically, each row in a typed table contains an instance of the named row type that is assigned to the table. In the preceding example, the fields of the person_t type define the columns of the person table.

Inserting data into a typed table is no different than inserting data into an untyped table. When you insert data into a typed table, the operation creates an instance of the row type and inserts it into the table. The following example shows how to insert a row into the person table:

The INSERT statement creates an instance of the person_t type and inserts it into the table. For information about how to insert, update, and delete columns that are defined on named row types, see "Modifying Columns That Contain Row Type Data".

You can use a single named row type to create multiple typed tables. In this case, each table has a unique name, but all tables share the same type.

Important: You cannot create a typed table that is a temporary table.
For information on the advantages of choosing to implement your data model using typed tables, see "Type Inheritance".

Converting an Untyped Table into a Typed Table

The primary advantage of typed tables over untyped tables is that typed tables can be used in an inheritance hierarchy. In general, inheritance allows a table to acquire the representation and behavior of another table. For more information about inheritance, see "What Is Inheritance?".

If you want to convert an existing untyped table into a typed table, you can use the ALTER TABLE statement. For example, consider the following untyped table:

To convert an untyped table to a typed table, both the field names and the field types of the named row type must match the column names and column types of the existing table. For example, to make the manager table a typed table, you must first create a named row type that matches the column definitions of the table. The following statement creates the manager_t type, which contains field names and field types that match the columns of the manager table:

Once you create the named row type that you want to assign to the existing untyped table, use the ALTER TABLE statement to assign the type to the table. The following statement alters the manager table and makes it a typed table of type manager_t:

The new manager table contains the same columns and data types as the old table but now provides the advantages of a typed table.

Using a Named Row Type to Create a Column

Both typed and untyped tables can contain columns that are defined on named row types. A column that is defined on a named row type behaves in the same way whether the column occurs in a typed table or untyped table. In the following example, the first statement creates a named row type address_t; the second statement assigns the address_t type to the address column in the employee table:

In the preceding CREATE TABLE statement, the address column has the street, city, state, and zip fields of the address_t type. Consequently, the employee table, which has only three columns, contains values for name, street, city, state, zip, and salary. You use dot notation to access the individual fields of a column that is defined on a row type. For information about using dot notation to access fields of a column, see "Field Projections".

When you insert data into a column that is assigned a row type, you need to use the ROW constructor to specify row literal values for the row type. The following example shows how to use the INSERT statement to insert a row into the employee table:

Strong typing is not enforced for an insert or update on a named row type. To ensure that the row values are of the named row type, you must explicitly cast to the named row type to generate values of a named row type, as shown in the previous example. The INSERT statement inserts three values, one of which is a row type value that contains four values. More specifically, the operation inserts unitary values for the name and salary columns, but it creates an instance of the address_t type and inserts it into the address column.

For more information about how to insert, update, and delete columns that are defined on row types, see "Modifying Columns That Contain Row Type Data".

Using a Named Row Type Within Another Named Row Type

You can use a row type as the data type of a field within another row type. In the following example, the first statement creates the address_t type, which is also used in the second statement to define the type of the address field of the employee_t type:

Important: A row type cannot be used recursively. If type_t is a row type, then type_t cannot be used as the data type of a field contained in type_t.

Dropping Named Row Types

To drop a named row type, use the DROP ROW TYPE statement. You can drop a type only if it has no dependencies. You cannot drop a named row type if any of the following conditions are true:

The following example shows how to drop the person_t type:

For information about dropping a named row type from a type hierarchy, see "Dropping Named Row Types from a Type Hierarchy".

Unnamed Row Types

An unnamed row type is a group of typed fields that you create with the ROW constructor. An important distinction between named and unnamed row types is that you cannot assign an unnamed row type to a table. You use an unnamed row type to define the type of a column or field only. In addition, an unnamed row type is identified by its structure alone, whereas a named row type is identified by its name. The structure of a row type consists of the number and data types of its fields. In general, it is easier to cast between unnamed row types than named row types because type checking on unnamed row types is by structural equivalence only.

The following statement assigns two unnamed row types to columns of the student table:

The s_name and s_address columns of the student table each contain multiple fields. Each field of an unnamed row type can have a different data type. Although the student table has only two columns, the unnamed row types define a total of seven fields: f_name, m_init, l_name, street, city, state, and zip.

The following example shows how to use the INSERT statement to insert data into the student table:

For more information about how to modify columns that are defined on row types, see "Modifying Columns That Contain Row Type Data".

The database server does not distinguish between two unnamed row types that contain the same number of fields and that have corresponding fields of the same type. Field names are irrelevant in type checking of unnamed row types. For example, the database server does not distinguish between the following unnamed row types:

For information on the syntax for unnamed row types, see the Data Type segment of the Informix Guide to SQL: Syntax. For information about how to cast row type values, see Chapter 13 in this manual.

Restrictions on Data Types Allowed in Unnamed Row Types

You cannot use the following data types in the field definition of an unnamed row type:

Collection Data Types

Collection data types enable you to store and manipulate collections of data within a single row of a table. A collection type has two components: a type constructor, which determines whether the collection type is a SET, MULTISET, or LIST, and an element type, which specifies the type of data that the collection can contain. (The SET, MULTISET, and LIST collection types are described in detail in the following sections.)

The elements of a collection can be of most any data type. (For a list of exceptions, see "Restrictions on Data Types Allowed in Collections".) The elements of a collection are the values that the collection contains. In a collection that contains the values: {'blue', 'green', 'yellow', and 'red'}, 'blue' represents a single element in the collection. Every element in a collection must be of the same type. For example, a collection whose element type is INTEGER can contain only integer values.

The element type of a collection can represent a single data type (column) or multiple data types (row). In the following example, the col_1 column represents a SET of integers:

To define a collection type that contains multiple data types, you can use a named row type or an unnamed row type. In the following example, the col_2 column represents a SET of rows that contain name and salary fields:

Once you define a column as a collection type, you can perform the following operations on the collection:

For information on the syntax that you use to create collection data types, see the Data Type segment of the Informix Guide to SQL: Syntax. For information about how to cast between collection data types, see Chapter 13 in this manual.

Important: The contents of a collection, including spaces and tabs, must not exceed 32 kilobytes.

Null Values in Collections

A collection cannot contain null elements. When you insert elements into a collection that is a row type, you must specify a value for at least one field of the row type for each element in the collection. For example, to insert data into col_2, you must provide, at minimum, a value for either the name or salary field. If you attempt to insert null values for both the name and salary fields, the database server returns an error.

Important: When you define a collection type, you must include the not null constraint as part of the type definition. No other column constraints are allowed on a collection type.

Using a Set

A set is an unordered collection of elements in which each element is unique. You define a column as a SET collection type when you want to store collections whose elements have the following characteristics:

To illustrate how you might use a SET, imagine that your human resources department needs information about the dependents of each employee in the company. You can use a collection type to define a column in an employee table that stores the names of an employee's dependents. The following statement creates a table in which the dependents column is defined as a SET:

A query against the dependents column for any given row returns the names of all the dependents of the employee. In this case, SET is the appropriate collection type because the collection of dependents for each employee should not contain any duplicate values. A column that is defined as a SET ensures that each element in a collection is unique.

To illustrate how to define a collection type whose elements are a row type, suppose that you want the dependents column to include the name and birthdate of an employee's dependents. In the following example, the dependents column is defined as a SET whose element type is a row type:

Each element of a collection from the dependents column contains values for the name and bdate. Each row of the employee table contains information about the employee as well as a collection with the names and birthdates of the employee's dependents. For example, if an employee has no dependents the collection for the dependents column is empty. If an employee has 10 dependents, the collection should contain 10 elements.

Using a Multiset

A multiset is a collection of elements in which elements can have duplicate values. For example, a multiset of integers might contain the collection {1,3,4,3,3}, which has duplicate elements. You can define a column as a MULTISET collection type when you want to store collections whose elements have the following characteristics:

To illustrate how you might use a MULTISET, suppose that your human resources department wants to keep track of the bonuses awarded to employees in the company. To track each employee's bonuses over time, you can use a MULTISET to define a column in a table that records all the bonuses that each employee receives. In the following example, the bonus column is a MULTISET:

You can use the bonus column in this statement to store and access the collection of bonuses for each employee. A query against the bonus column for any given row returns the dollar amount for each bonus that the employee has received. Because an employee might receive multiple bonuses of the same amount (resulting in a collection whose elements are not all unique), the bonus column is defined as a MULTISET, which allows duplicate values.

Using a List

A list is an ordered collection of elements that allows duplicate values. A list differs from a MULTISET in that each element in a list has an ordinal position in the collection. The order of the elements in a list corresponds with the order in which values are inserted into the LIST. You can define a column as a LIST collection type when you want to store collections whose elements have the following characteristics:

To illustrate how you might use a LIST, suppose your sales department wants to keep a monthly record of the sales total for each salesperson. You can use a LIST to define a column in a table that contains the monthly sales totals for each salesperson. The following example creates a table in which the month_sales column is a LIST. The first entry (element) in the LIST, with an ordinal position of 1, might correspond to the month of January, the second element, with an ordinal position of 2, February, and so forth.

You can use the month_sales column in this statement to store and access the monthly sales totals for each salesperson. More specifically, you might perform queries on the month_sales column to find out:

Nesting Collection Types

A nested collection is a collection type that contains another collection type. You can nest any collection type within another collection type. There is no practical limit on how deeply you can nest a collection type. However, performing inserts or updates on a collection that has been nested more than one or two levels can be difficult. The following example shows several ways in which you might create columns that are defined on nested collection types:

For information about how to access a nested collection, see "Modifying Collections".

Adding a Collection Type to an Existing Table

You can use the ALTER TABLE statement to add or drop a column that is a collection type (or any other data type). For example, the following statement adds the flowers column, which is defined as a SET, to the nursery table:

You cannot modify an existing column that is a collection type or convert a non-collection type column into a collection type.

For more information on adding and dropping collection-type columns, see the ALTER TABLE statement in the Informix Guide to SQL: Syntax.

Important: You cannot use the ALTER TABLE statement to add a column to a typed table because the named row type that is assigned to the table specifies the structure of the table.

Restrictions on Data Types Allowed in Collections

You cannot use either of the following data types as the element type of a collection:




Informix Guide to SQL: Tutorial, version 9.1
Copyright © 1998, Informix Software, Inc. All rights reserved.