Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
50
Добавлен:
16.04.2013
Размер:
5.97 Mб
Скачать

Avoiding Collection Exceptions

n NumList := NumList(10,20,30,40,50,60,70,80,90,100);

TYPE NickList IS TABLE OF VARCHAR2(64) INDEX BY VARCHAR2(32); nicknames NickList;

BEGIN

 

 

n.DELETE(2);

-- deletes element 2

 

n.DELETE(3,6);

-- deletes elements 3

through 6

n.DELETE(7,7);

-- deletes element 7

 

n.DELETE(6,3);

-- does nothing since

6 > 3

n.DELETE;

-- deletes all elements

nicknames('Bob') := 'Robert'; nicknames('Buffy') := 'Esmerelda'; nicknames('Chip') := 'Charles'; nicknames('Dan') := 'Daniel'; nicknames('Fluffy') := 'Ernestina'; nicknames('Rob') := 'Robert';

nicknames.DELETE('Chip'); -- deletes element denoted by this key nicknames.DELETE('Buffy','Fluffy'); -- deletes elements with keys in this

alphabetic range END;

/

Varrays always have consecutive subscripts, so you cannot delete individual elements except from the end (by using the TRIM method).

If an element to be deleted does not exist, DELETE simply skips it; no exception is raised. PL/SQL keeps placeholders for deleted elements, so you can replace a deleted element by assigning it a new value.

DELETE lets you maintain sparse nested tables. You can store sparse nested tables in the database, just like any other nested tables.

The amount of memory allocated to a nested table can increase or decrease dynamically. As you delete elements, memory is freed page by page. If you delete the entire table, all the memory is freed.

Applying Methods to Collection Parameters

Within a subprogram, a collection parameter assumes the properties of the argument bound to it. You can apply the built-in collection methods (FIRST, LAST, COUNT, and so on) to such parameters. You can create general-purpose subprograms that take collection parameters and iterate through their elements, add or delete elements, and so on.

Note: For varray parameters, the value of LIMIT is always derived from the parameter type definition, regardless of the parameter mode.

Avoiding Collection Exceptions

In most cases, if you reference a nonexistent collection element, PL/SQL raises a predefined exception. Consider the following example:

DECLARE

 

 

TYPE NumList IS TABLE OF NUMBER;

 

nums NumList;

-- atomically null

 

BEGIN

 

 

/* Assume execution continues despite the raised exceptions. */

 

nums(1) := 1;

-- raises COLLECTION_IS_NULL

(1)

5-30 PL/SQL User's Guide and Reference

Avoiding Collection Exceptions

nums := NumList(1,2); nums(NULL) := 3; nums(0) := 3; nums(3) := 3; nums.DELETE(1);

IF nums(1) = 1 THEN NULL; END IF; END;

/

--

initialize nested table

 

--

raises VALUE_ERROR

(2)

--raises SUBSCRIPT_OUTSIDE_LIMIT (3)

--raises SUBSCRIPT_BEYOND_COUNT (4)

--delete element 1

-- raises NO_DATA_FOUND

(5)

In the first case, the nested table is atomically null. In the second case, the subscript is null. In the third case, the subscript is outside the allowed range. In the fourth case, the subscript exceeds the number of elements in the table. In the fifth case, the subscript designates a deleted element.

The following list shows when a given exception is raised:

Collection Exception

Raised when...

 

 

COLLECTION_IS_NULL

you try to operate on an atomically null collection.

NO_DATA_FOUND

a subscript designates an element that was deleted, or a

 

nonexistent element of an associative array.

SUBSCRIPT_BEYOND_COUNT

a subscript exceeds the number of elements in a

 

collection.

SUBSCRIPT_OUTSIDE_LIMIT a subscript is outside the allowed range.

VALUE_ERROR a subscript is null or not convertible to the key type. This exception might occur if the key is defined as a PLS_ INTEGER range, and the subscript is outside this range.

In some cases, you can pass invalid subscripts to a method without raising an exception. For instance, when you pass a null subscript to procedure DELETE, it does nothing. You can replace deleted elements by assigning values to them, without raising NO_DATA_FOUND:

DECLARE

 

 

TYPE NumList IS TABLE OF NUMBER;

 

nums NumList := NumList(10,20,30);

-- initialize table

BEGIN

 

 

nums.DELETE(-1);

-- does not raise SUBSCRIPT_OUTSIDE_LIMIT

nums.DELETE(3);

-- delete 3rd element

dbms_output.put_line(nums.COUNT);

-- prints 2

nums(3) := 30;

-- allowed; does not raise NO_DATA_FOUND

dbms_output.put_line(nums.COUNT);

-- prints 3

END;

 

 

/

 

 

Packaged collection types and local collection types are never compatible. For example, suppose you want to call the following packaged procedure:

CREATE PACKAGE pkg

AS

 

TYPE NumList IS

TABLE OF NUMBER;

 

PROCEDURE print_numlist (nums NumList);

END pkg;

 

 

/

 

 

 

DECLARE

 

 

TYPE NumList IS

TABLE OF NUMBER;

 

n1

pkg.NumList

:= pkg.NumList(2,4); -- Type from the package.

n2

NumList := NumList(6,8);

-- Local type.

Using PL/SQL Collections and Records 5-31

What Is a PL/SQL Record?

BEGIN pkg.print_numlist(n1);

--The packaged procedure can't accept a value of the local type. pkg.print_numlist(n2); -- Causes a compilation error.

END;

/

DROP PACKAGE pkg;

The second procedure call fails, because the packaged and local VARRAY types are incompatible despite their identical definitions.

What Is a PL/SQL Record?

A record is a group of related data items stored in fields, each with its own name and datatype. You can think of a record as a variable that can hold a table row, or some columns from a table row. The fields correspond to table columns.

The %ROWTYPE attribute lets you declare a record that represents a row in a database table, without listing all the columns. Your code keeps working even after columns are added to the table. If you want to represent a subset of columns in a table, or columns from different tables, you can define a view or declare a cursor to select the right columns and do any necessary joins, and then apply %ROWTYPE to the view or cursor.

Defining and Declaring Records

To create records, you define a RECORD type, then declare records of that type. You can also create or find a table, view, or PL/SQL cursor with the values you want, and use the %ROWTYPE attribute to create a matching record.

You can define RECORD types in the declarative part of any PL/SQL block, subprogram, or package. When you define your own RECORD type, you can specify a NOT NULL constraint on fields, or give them default values.

DECLARE

-- Declare a record type with 3 fields.

TYPE rec1_t IS RECORD (field1 VARCHAR2(16), field2 NUMBER, field3 DATE); -- For any fields declared NOT NULL, we must supply a default value.

TYPE rec2_t IS RECORD (id INTEGER NOT NULL := -1, name VARCHAR2(64) NOT NULL := '[anonymous]');

--Declare record variables of the types declared above. rec1 rec1_t;

rec2 rec2_t;

--Declare a record variable that can hold a row from the EMPLOYEES table.

--The fields of the record automatically match the names and types of the columns.

--Don't need a TYPE declaration in this case.

rec3 employees%ROWTYPE;

-- Or we can mix fields that are table columns with user-defined fields. TYPE rec4_t IS RECORD (first_name employees.first_name%TYPE, last_name

employees.last_name%TYPE, rating NUMBER); rec4 rec4_t;

BEGIN

-- Read and write fields using dot notation rec1.field1 := 'Yesterday';

5-32 PL/SQL User's Guide and Reference

Defining and Declaring Records

rec1.field2 := 65;

rec1.field3 := TRUNC(SYSDATE-1);

-- We didn't fill in the NAME field, so it takes the default value declared above. dbms_output.put_line(rec2.name);

END;

/

To store a record in the database, you can specify it in an INSERT or UPDATE statement, if its fields match the columns in the table:

...

You can use %TYPE to specify a field type corresponding to a table column type. Your code keeps working even if the column type is changed (for example, to increase the length of a VARCHAR2 or the precision of a NUMBER). The following example defines RECORD types to hold information about a department:

DECLARE

--Best: use %ROWTYPE instead of specifying each column.

--Using <cursor>%ROWTYPE instead of <table>%ROWTYPE since we only want some columns.

--Declaring the cursor doesn't run the query, so no performance hit. CURSOR c1 IS SELECT department_id, department_name, location_id FROM

departments;

rec1 c1%ROWTYPE;

-- Use <column>%TYPE in field declarations to avoid problems if the column types change.

TYPE DeptRec2 IS RECORD (dept_id departments.department_id%TYPE, dept_name departments.department_name%TYPE, dept_loc departments.location_id%TYPE);

rec2 DeptRec2;

--Final technique, writing out each field name and specifying the type directly,

is

--clumsy and unmaintainable for working with table data. Use only for all-PL/SQL code.

TYPE DeptRec3 IS RECORD (dept_id NUMBER, dept_name VARCHAR2(14), dept_loc VARCHAR2(13));

rec3 DeptRec3; BEGIN

NULL;

END;

/

PL/SQL lets you define records that contain objects, collections, and other records (called nested records). However, records cannot be attributes of object types.

Using Records as Procedure Parameters and Function Return Values

Records are easy to process using stored procedures because you can pass just one parameter, instead of a separate parameter for each field. For example, you might fetch a table row from the EMPLOYEES table into a record, then pass that row as a parameter to a function that computed that employee's vacation allowance or some other abstract value. The function could access all the information about that employee by referring to the fields in the record.

The next example shows how to return a record from a function. To make the record type visible across multiple stored functions and stored procedures, declare the record type in a package specification.

Using PL/SQL Collections and Records 5-33

Соседние файлы в папке Oracle 10g