L> Collections and Recordns PL/SQL User"s Guide and ReferenceRelease 8.1.6Part Number A77069-01LibraryProductContentsIndex 4 Collections and RecordsKnowledge is that area of ignorance that we arrange and classify. --Ambrose BierceIncreasingly, programmers are using collection types such as arrays, bags, lists, nested tables, sets, and trees in traditional database applications. To meet the growing demand, PL/SQL provides the datatypes TABLE and VARRAY, which allow you to declare index-by tables, nested tables and variable-size arrays. In this chapter, you learn how those types let you reference and manipulate collections of data as whole objects. You also learn how the datatype RECORD lets you treat related but dissimilar data as a logical unit.Major TopicsWhat Is a Collection?Defining and Declaring CollectionsInitializing and Referencing CollectionsAssigning and Comparing CollectionsManipulating CollectionsUsing Collection MethodsAvoiding Collection ExceptionsTaking Advantage of Bulk BindsWhat Is a Record?Defining and Declaring RecordsInitializing and Referencing RecordsAssigning and Comparing RecordsManipulating Records What Is a Collection?A collection is an ordered group of elements, all of the same type (for example, the grades for a class of students). Each element has a unique subscript that determines its position in the collection. PL/SQL offers two collection types. Items of type TABLE are either index-by tables (Version 2 PL/SQL tables) or nested tables (which extend the functionality of index-by tables). Items of type VARRAY are varrays (short for variable-size arrays).Collections work like the arrays found in most third-generation programming languages. However, collections can have only one dimension and must be indexed by integers. (In some languages such as Ada and Pascal, arrays can have multiple dimensions and can be indexed by enumeration types.) You can define collection types in a package, then use them programmatically in your applications. Also, you can pass collections as parameters. So, you can use them to move columns of data into and out of database tables or between client-side applications and stored subprograms. In addition, collections can store instances of an object type and (except for index-by tables) can be attributes of an object type. Understanding Nested TablesWithin the database, nested tables can be considered one-column database tables. starrkingschool.net stores the rows of a nested table in no particular order. But, when you retrieve the nested table into a PL/SQL variable, the rows are given consecutive subscripts starting at 1. That gives you array-like access to individual rows. Within PL/SQL, nested tables are like one-dimensional arrays. However, nested tables differ from arrays in two important ways. First, arrays have a fixed upper bound, but nested tables are unbounded (see Figure4-1). So, the size of a nested table can increase dynamically. Figure 4-1 Array versus Nested Table
*
Second, arrays must be dense (have consecutive subscripts). So, you cannot delete individual elements from an array. Initially, nested tables are dense, but they can be sparse (have nonconsecutive subscripts). So, you can delete elements from a nested table using the built-in procedure DELETE. That might leave gaps in the index, but the built-in function NEXT lets you iterate over any series of subscripts. Nested Tables versus Index-by TablesIndex-by tables and nested tables are similar. For example, they have the same structure, and their individual elements are accessed in the same way (using subscript notation). The main difference is that nested tables can be stored in a database column (hence the term "nested table") but index-by tables cannot.Nested tables extend the functionality of index-by tables by letting you SELECT, INSERT, UPDATE, and DELETE nested tables stored in the database. (Remember, index-by tables cannot be stored in the database.) Also, some collection methods operate only on nested tables and varrays. For example, the built-in procedure TRIM cannot be applied to index-by tables.Another advantage of nested tables is that an uninitialized nested table is atomically null (that is, the table itself is null, not its elements), but an uninitialized index-by table is merely empty. So, you can apply the IS NULL comparison operator to nested tables but not to index-by tables.However, index-by tables also have some advantages. For example, PL/SQL supports implicit (automatic) datatype conversion between host arrays and index-by tables (but not nested tables). So, the most efficient way to pass collections to and from the database server is to use anonymous PL/SQL blocks to bulk-bind input and output host arrays to index-by tables.Also, index-by tables are initially sparse. So, they are convenient for storing reference data using a numeric primary key (account numbers or employee numbers for example) as the index.In some (relatively minor) ways, index-by tables are more flexible than nested tables. For example, subscripts for a nested table are unconstrained. In fact, index-by tables can have negative subscripts (nested tables cannot). Also, some element types are allowed for index-by tables but not for nested tables (see "Referencing Collection Elements"). Finally, to extend a nested table, you must use the built-in procedure EXTEND, but to extend an index-by table, you just specify larger subscripts. Understanding VarraysItems of type VARRAY are called varrays. They allow you to associate a single identifier with an entire collection. This association lets you manipulate the collection as a whole and reference individual elements easily. To reference an element, you use standard subscripting syntax (see Figure4-2). For example, Grade(3) references the third element in varray Grades. Figure 4-2 Varray of Size 10
*
A varray has a maximum size, which you must specify in its type definition. Its index has a fixed lower bound of 1 and an extensible upper bound. For example, the current upper bound for varray Grades is 7, but you can extend it to 8, 9, or 10. Thus, a varray can contain a varying number of elements, from zero (when empty) to the maximum specified in its type definition. Varrays versus Nested TablesNested tables differ from varrays in the following ways:Varrays have a maximum size, but nested tables do not.Varrays are always dense, but nested tables can be sparse. So, you can delete individual elements from a nested table but not from a varray. starrkingschool.net stores varray data in-line (in the same table) unless it exceeds 4K, in which case the data is stored out-of-line (but in the same tablespace). But, starrkingschool.net stores nested table data out-of-line in a store table, which is a system-generated database table associated with the nested table. When stored in the database, varrays retain their ordering and subscripts, but nested tables do not.Which collection type should you use? That depends on your wants and the size of the collection. A varray is stored as an opaque object, whereas a nested table is stored in a storage table with every element mapped to a row in the table. So, if you want efficient queries, use nested tables. If you want to retrieve entire collections as a whole, use varrays. However, when collections get very large, it becomes impractical to retrieve more than subsets. So, varrays are better suited for small collections. Defining and Declaring CollectionsTo create collections, you define a collection type, then declare collections of that type. You can define TABLE and VARRAY types in the declarative part of any PL/SQL block, subprogram, or package. For nested tables, use the syntax TYPE type_name IS TABLE OF element_type ; and for varrays, use the following syntax: TYPE type_name IS VARRAY (size_limit) OF element_type ; where type_name is a type specifier used later to declare collections, size_limit is a positive integer literal, and element_type is any PL/SQL datatype except BINARY_INTEGER, PLS_INTEGERBOOLEANBLOB, CLOB (restriction applies only to varrays)LONG, LONG RAWNATURAL, NATURALNNCHAR, NCLOB, NVARCHAR2object types with BLOB or CLOB attributes (restriction applies only to varrays)object types with TABLE or VARRAY attributesPOSITIVE, POSITIVENREF CURSORSIGNTYPESTRINGTABLEVARRAYIf element_type is a record type, every field in the record must be a scalar type or an object type.For index-by tables, use the syntaxation TYPE type_name IS TABLE OF element_type INDEX BY BINARY_INTEGER; Unlike nested tables and varrays, index-by tables can have the following element types: BINARY_INTEGER, BOOLEAN, LONG, LONG RAW, NATURAL, NATURALN, PLS_INTEGER, POSITIVE, POSITIVEN, SIGNTYPE, and STRING.Index-by tables are initially sparse. That enables you, for example, to store reference data in a temporary index-by table using a numeric primary key as the index. In the example below, you declare an index-by table of records. Each element of the table stores a row from the emp database table. DECLARE TYPE EmpTabTyp IS TABLE OF emp%ROWTYPE INDEX BY BINARY_INTEGER; emp_tab EmpTabTyp;BEGIN /* Retrieve employee record. */ SELECT * INTO emp_tab(7468) FROM emp WHERE empno = 7468; ...END; When defining a VARRAY type, you must specify its maximum size. In the following example, you define a type that stores up to 366 dates: DECLARE TYPE Calendar IS VARRAY(366) OF DATE; To specify the element type, you can use %TYPE, which provides the datatype of a variable or database column. Also, you can use %ROWTYPE, which provides the rowtype of a cursor or database table. Two examples follow: DECLARE TYPE EmpList IS TABLE OF emp.ename%TYPE; -- based on column CURSOR c1 IS SELECT * FROM dept; TYPE DeptFile IS VARRAY(20) OF c1%ROWTYPE; -- based on cursor In the next example, you use a RECORD type to specify the element type: DECLARE TYPE AnEntry IS RECORD ( term VARCHAR2(20), meaning VARCHAR2(200)); TYPE Glossary IS VARRAY(250) OF AnEntry; In the final example, you impose a NOT NULL constraint on the element type: DECLARE TYPE EmpList IS TABLE OF emp.empno%TYPE NOT NULL; An initialization clause is not required (or allowed). Declaring CollectionsOnce you define a collection type, you can declare collections of that type, as the following SQL*Plus script shows: CREATE TYPE CourseList AS TABLE OF VARCHAR2(10) -- define type/CREATE TYPE Student AS OBJECT ( -- create object id_num INTEGER(4), name VARCHAR2(25), address VARCHAR2(35), status CHAR(2), courses CourseList) -- declare nested table as attribute/ The identifier courses represents an entire nested table. Each element of courses will store the code name of a college course such as "Math 1020". The script below creates a database column that stores varrays. Each element of the varrays will store a Project object. CREATE TYPE Project AS OBJECT( --create object project_no NUMBER(2), title VARCHAR2(35), cost NUMBER(7,2))/CREATE TYPE ProjectList AS VARRAY(50) OF Project -- define VARRAY type/CREATE TABLE department ( -- create database table dept_id NUMBER(2), name VARCHAR2(15), budget NUMBER(11,2), projects ProjectList) -- declare varray as column/ The following example shows that you can use %TYPE to provide the datatype of a previously declared collection: DECLARE TYPE Platoon IS VARRAY(20) OF Soldier; p1 Platoon; p2 p1%TYPE; You can declare collections as the formal parameters of functions and procedures. That way, you can pass collections to stored subprograms and from one subprogram to another. In the following example, you declare a nested table as the formal parameter of a packaged procedure: CREATE PACKAGE personnel AS TYPE Staff IS TABLE OF Employee; ... PROCEDURE award_bonuses (members IN Staff);END personnel; Also, you can specify a collection type in the RETURN clause of a function specification, as the following example shows: DECLARE TYPE SalesForce IS VARRAY(25) OF Salesperson; FUNCTION top_performers (n INTEGER) RETURN SalesForce IS ... Collections follow the usual scoping and instantiation rules. In a block or subprogram, collections are instantiated when you enter the block or subprogram and cease to exist when you exit. In a package, collections are instantiated when you first reference the package and cease to exist when you end the database session. Initializing and Referencing CollectionsUntil you initialize it, a nested table or varray is atomically null (that is, the collection itself is null, not its elements). To initialize a nested table or varray, you use a constructor, which is a system-defined function with the same name as the collection type. This function "constructs" collections from the elements passed to it. In the following example, you pass six elements to constructor CourseList(), which returns a nested table containing those elements: DECLARE my_courses CourseList;BEGIN my_courses := CourseList("Econ 2010", "Acct 3401", "Mgmt 3100", "PoSc 3141", "Mktg 3312", "Engl 2005"); ...END; In the next example, you pass three objects to constructor ProjectList(), which returns a varray containing those objects: DECLARE accounting_projects ProjectList;BEGIN accounting_projects := ProjectList(Project(1, "Design New Expense Report", 3250), Project(2, "Outsource Payroll", 12350), Project(3, "Audit Accounts Payable", 1425)); ...END; You need not initialize the whole varray. For example, if a varray has a maximum size of 50, you can pass fewer than 50 elements to its constructor. Unless you impose the NOT NULL constraint or specify a record type for elements, you can pass null elements to a constructor. An example follows: BEGIN my_courses := CourseList("Math 3010", NULL, "Stat 3202", ...); The next example shows that you can initialize a collection in its declaration, which is a good programming practice: DECLARE my_courses CourseList := CourseList("Art 1111", "Hist 3100", "Engl 2005", ...); If you call a constructor without arguments, you get an empty but non-null collection, as the following example shows: DECLARE TYPE Clientele IS VARRAY(100) OF Customer; vips Clientele := Clientele(); -- initialize empty varray BEGIN IF vips IS NOT NULL THEN -- condition yields TRUE ... END IF;END; PL/SQL never calls a constructor implicitly, so you must call it explicitly. (That does not apply to index-by tables, which do not have constructors.) Constructor calls are allowed wherever function calls are allowed.In the example below, you insert a Student object into object table sophomores. The table constructor CourseList() provides a value for attribute courses. BEGIN INSERT INTO sophomores VALUES (Student(5035, "Janet Alvarez", "122 Broad St", "FT", CourseList("Econ 2010", "Acct 3401", "Mgmt 3100", ...))); ... In the final example, you insert a row into database table department. The varray constructor ProjectList() provides a value for column projects. BEGIN INSERT INTO department VALUES(60, "Security", 750400, ProjectList(Project(1, "Issue New Employee Badges", 9500), Project(2, "Find Missing IC Chips", 2750), Project(3, "Inspect Emergency Exits", 1900))); ... Referencing Collection ElementsEvery reference to an element includes a collection name and a subscript enclosed in parentheses. The subscript determines which element is processed. To reference an element, you specify its subscript using the syntaxation collection_name(subscript) where subscript is an expression that yields an integer. For index-by tables, the legal subscript range is -2**31 .. 2**31. For nested tables, the legal range is 1 .. 2**31. And, for varrays, the legal range is 1 .. size_limit.You can reference a collection in all expression contexts. In the following example, you reference an element in nested table names: DECLARE TYPE Roster IS TABLE OF VARCHAR2(15); names Roster := Roster("J Hamil", "D Caruso", "R Singh", ...); i BINARY_INTEGER;BEGIN ... IF names(i) = "J Hamil" THEN ... END IF;END; The next example shows that you can reference the elements of a collection in subprogram calls: DECLARE TYPE Roster IS TABLE OF VARCHAR2(15); names Roster := Roster("J Hamil", "D Piro", "R Singh", ...); i BINARY_INTEGER;BEGIN ... verify_name(names(i)); -- call procedureEND; When calling a function that returns a collection, use the following syntax to reference elements in the collection: function_name(parameter_list)(subscript) For example, the following call references the third element in the varray returned by function new_hires: DECLARE TYPE Staff IS VARRAY(20) OF Employee; staffer Employee; FUNCTION new_hires (hiredate DATE) RETURN Staff IS ...BEGIN staffer := new_hires("16-OCT-96")(3); -- call attribute ...END; Assigning and Comparing CollectionsOne collection can be assigned to another by an INSERT, UPDATE, FETCH, or SELECT statement, an assignment statement, or a subprogram call. As the example below shows, the collections must have the same datatype. Having the same element type is not enough. DECLARE TYPE Clientele IS VARRAY(100) OF Customer; TYPE Vips IS VARRAY(100) OF Customer; group1 Clientele := Clientele(...); group2 Clientele := Clientele(...); group3 Vips := Vips(...);BEGIN group2 := group1; group3 := group2; -- illegal; different datatypes If you assign an atomically null collection to another collection, the other collection becomes atomically null (and must be reinitialized). Consider the following example: DECLARE TYPE Clientele IS TABLE OF Customer; group1 Clientele := Clientele(...); -- initialized group2 Clientele; -- atomically nullBEGIN IF group1 IS NULL THEN ... -- condition yields FALSE group1 := group2; IF group1 IS NULL THEN ... -- condition yields TRUE ...END; Likewise, if you assign the non-value NULL to a collection, the collection becomes atomically null. Assigning Collection ElementsYou can assign the value of an expression to a specific element in a collection using the syntaxation collection_name(subscript) := expression; where expression yields a value of the type specified for elements in the collection type definition. If subscript is null or not convertible to an integer, PL/SQL raises the predefined exception VALUE_ERROR. If the subscript refers to an uninitialized elementt, PL/SQL raises SUBSCRIPT_BEYOND_COUNT. If the collection is atomically null, PL/SQL raises COLLECTION_IS_NULL. Some examples follow: DECLARE TYPE NumList IS TABLE OF INTEGER; nums NumList;BEGIN /* Assume execution continues despite the raised exceptions. */ nums(1) := 10; -- raises COLLECTION_IS_NULL nums := NumList(10,20,30); nums(1) := ASCII("A"); nums(2) := 10 * nums(1); nums("B") := 15; -- raises VALUE_ERROR nums(4) := 40; -- raises SUBSCRIPT_BEYOND_COUNTEND; Comparing Whole CollectionsNested tables and varrays can be atomically null, so they can be tested for nullity, as the following example shows: DECLARE TYPE Staff IS TABLE OF Employee; members Staff;BEGIN ... IF members IS NULL THEN ... -- condition yields TRUE;END; However, collections cannot be compared for equality or inequality. For instance, the following IF condition is illegal: DECLARE TYPE Clientele IS TABLE OF Customer; group1 Clientele := Clientele(...); group2 Clientele := Clientele(...); BEGIN ... IF group1 = group2 THEN -- causes compilation error ... END IF;END; This restriction also applies to implicit comparisons. For example, collections cannot appear in a DISTINCT, GROUP BY, or ORDER BY list. Manipulating CollectionsWithin PL/SQL, collections add flexibility and procedural power. A big advantage is that your program can compute subscripts to process specific elements. A bigger advantage is that the program can use SQL to manipulate in-memory collections. Some Nested Table Examples In SQL*Plus, suppose you define object type Course, as follows: SQL> CREATE TYPE Course AS OBJECT ( 2 course_no NUMBER(4), 3 title VARCHAR2(35), 4 credits NUMBER(1)); Next, you define TABLE type CourseList, which stores Course objects: SQL> CREATE TYPE CourseList AS TABLE OF Course; Finally, you create database table department, which has a column of type CourseList, as follows: SQL> CREATE TABLE department ( 2 name VARCHAR2(20), 3 director VARCHAR2(20), 4 office VARCHAR2(20), 5 courses CourseList) 6 NESTED TABLE courses STORE AS courses_tab; Each item in column courses is a nested table that will store the courses offered by a given department. The NESTED TABLE clause is required because department has a nested table column. The clause identifies the nested table and names a system-generated store table, in which starrkingschool.net stores data out-of-line.Now, you can populate database table department. In the following example, notice how table constructor CourseList() provides values for column courses: BEGIN INSERT INTO department VALUES("Psychology", "Irene Friedman", "Fulton Hall 133", CourseList(Course(1000, "General Psychology", 5), Course(2100, "Experimental Psychology", 4), Course(2200, "Psychological Tests", 3), Course(2250, "Behavior Modification", 4), Course(3540, "Groups and Organizations", 3), Course(3552, "Human Factors in Busines", 4), Course(4210, "Theories of Learning", 4), Course(4320, "Cognitive Processes", 4), Course(4410, "Abnormal Psychology", 4))); INSERT INTO department VALUES("History", "John Whalen", "Applegate Hall 142", CourseList(Course(1011, "History of Europe I", 4), Course(1012, "History of Europe II", 4), Course(1202, "American History", 5), Course(2130, "The Renaissance", 3), Course(2132, "The Reformation", 3), Course(3105, "History of Ancient Greece", 4), Course(3321, "Early Japan", 4), Course(3601, "Latin America Since 1825", 4), Course(3702, "Medieval Islamic History", 4))); INSERT INTO department VALUES("English", "Lynn Saunders", "Breakstone Hall 205", CourseList(Course(1002, "Expository Writing", 3), Course(2020, "Film and Literature", 4), Course(2418, "Modern Science Fiction", 3), Course(2810, "Discursive Writing", 4), Course(3010, "Modern English Grammar", 3), Course(3720, "Introduction to Shakespeare", 4), Course(3760, "Modern Drama", 4), Course(3822, "The Short Story", 4), Course(3870, "The American Novel", 5)));END; In the following example, you revise the list of courses offered by the English Department: DECLARE new_courses CourseList := CourseList(Course(1002, "Expository Writing", 3), Course(2020, "Film and Literature", 4), Course(2810, "Discursive Writing", 4), Course(3010, "Modern English Grammar", 3), Course(3550, "Realism and Naturalism", 4), Course(3720, "Introduction to Shakespeare", 4), Course(3760, "Modern Drama", 4), Course(3822, "The Short Story", 4), Course(3870, "The American Novel", 4), Course(4210, "20th-Century Poetry", 4), Course(4725, "Advanced Workshop in Poetry", 5));BEGIN UPDATE room SET courses = new_courses WHERE name = "English";END; In the next example, you retrieve all the courses offered by the Psychology Department into a local nested table: DECLARE psyc_courses CourseList;BEGIN SELECT courses INTO psyc_courses FROM department WHERE name = "Psychology"; ...END; Some Varray ExamplesIn SQL*Plus, suppose you define object type Project, as follows: SQL> CREATE TYPE Project AS OBJECT ( 2 project_no NUMBER(2), 3 title VARCHAR2(35), 4 cost NUMBER(7,2)); Next, you define VARRAY type ProjectList, which stores Project objects: SQL> CREATE TYPE ProjectList AS VARRAY(50) OF Project; Finally, you create relational table department, which has a column of type ProjectList, as follows: SQL> CREATE TABLE department ( 2 dept_id NUMBER(2), 3 name VARCHAR2(15), 4 budget NUMBER(11,2), 5 projects ProjectList); Each item in column projects is a varray that will store the projects scheduled for a given department. Now, you are ready to populate relational table department. In the following example, notice how varray constructor ProjectList() provides values for column projects: BEGIN INSERT INTO department VALUES(30, "Accounting", 1205700, ProjectList(Project(1, "Design New Expense Report", 3250), Project(2, "Outsource Payroll", 12350), Project(3, "Evaluate Merger Proposal", 2750), Project(4, "Audit Accounts Payable", 1425))); INSERT INTO department VALUES(50, "Maintenance", 925300, ProjectList(Project(1, "Repair Leak in Roof", 2850), Project(2, "Install New Door Locks", 1700), Project(3, "Wash Front Windows", 975), Project(4, "Repair Faulty Wiring", 1350), Project(5, "Winterize Cooling System", 1125))); INSERT INTO department VALUES(60, "Security", 750400, ProjectList(Project(1, "Issue New Employee Badges", 13500), Project(2, "Find Missing IC Chips", 2750), Project(3, "Upgrade Alarm System", 3350), Project(4, "Inspect Emergency Exits", 1900)));END; In the following example, you update the list of projects assigned to the Security Department: DECLARE new_projects ProjectList := ProjectList(Project(1, "Issue New Employee Badges", 13500), Project(2, "Develop New Patrol Plan", 1250), Project(3, "Inspect Emergency Exits", 1900), Project(4, "Upgrade Alarm System", 3350), Project(5, "Analyze Local Crime Stats", 825));BEGIN UPDATE room SET projects = new_projects WHERE dept_id = 60;END; In the next example, you retrieve all the projects for the Accounting Department into a local varray: DECLARE my_projects ProjectList;BEGIN SELECT projects INTO my_projects FROM department WHERE dept_id = 30; ...END; In the final example, you delete the Accounting Department and its project list from table department: BEGIN DELETE FROM department WHERE dept_id = 30;END; Manipulating Individual ElementsSo far, you have manipulated whole collections. Within SQL, to manipulate the individual elements of a collection, use the operator TABLE. The operand of TABLE is a subquery that returns a single column value for you to manipulate. That value is a nested table or varray.In the following example, you add a row to the History Department nested table stored in column courses: BEGIN INSERT INTO TABLE(SELECT courses FROM department WHERE name = "History") VALUES(3340, "Modern China", 4);END; In the next example, you revise the number of credits for two courses offered by the Psychology Department: DECLARE adjustment INTEGER DEFAULT 1;BEGIN UPDATE TABLE(SELECT courses FROM department WHERE name = "Psychology") SET credits = credits + adjustment WHERE course_no IN (2200, 3540);END; In the following example, you retrieve the number and title of a specific course offered by the History Department: DECLARE my_course_no NUMBER(4); my_title VARCHAR2(35);BEGIN SELECT course_no, title INTO my_course_no, my_title FROM TABLE(SELECT courses FROM room WHERE name = "History") WHERE course_no = 3105; ...END; In the next example, you delete all 5-credit courses offered by the English Department: BEGIN DELETE TABLE(SELECT courses FROM department WHERE name = "English") WHERE credits = 5;END; In the following example, you retrieve the title and cost of the Maintenance Department"s fourth project from the varray column projects: DECLARE my_cost NUMBER(7,2); my_title VARCHAR2(35);BEGIN SELECT cost, title INTO my_cost, my_title FROM TABLE(SELECT projects FROM department WHERE dept_id = 50) WHERE project_no = 4; ...END; Currently, you cannot reference the individual elements of a varray in an INSERT, UPDATE, or DELETE statement. So, you must use PL/SQL procedural statements. In the following example, stored procedure add_project inserts a new project into a department"s project list at a given position: CREATE PROCEDURE add_project ( dept_no IN NUMBER, new_project IN Project, position IN NUMBER) AS my_projects ProjectList;BEGIN SELECT projects INTO my_projects FROM department WHERE dept_no = dept_id FOR UPDATE OF projects; my_projects.EXTEND; -- make room for new project /* Move varray elements forward. */ FOR i IN REVERSE position..my_projects.LAST - 1 LOOP my_projects(i + 1) := my_projects(i); END LOOP; my_projects(position) := new_project; -- add new project UPDATE department SET projects = my_projects WHERE dept_no = dept_id;END add_project; The following stored procedure updates a given project: CREATE PROCEDURE update_project ( dept_no IN NUMBER, proj_no IN NUMBER, new_title IN VARCHAR2 DEFAULT NULL, new_cost IN NUMBER DEFAULT NULL) AS my_projects ProjectList;BEGIN SELECT projects INTO my_projects FROM room WHERE dept_no = dept_id FOR UPDATE OF projects; /* Find project, update it, then exit loop immediately. */ FOR i IN my_projects.FIRST..my_projects.LAST LOOP IF my_projects(i).project_no = proj_no THEN IF new_title IS NOT NULL THEN my_projects(i).title := new_title; END IF; IF new_cost IS NOT NULL THEN my_projects(i).cost := new_cost; END IF; EXIT; END IF; END LOOP; UPDATE department SET projects = my_jobs WHERE dept_no = dept_id; END update_project; Manipulating Local CollectionsWithin PL/SQL, to manipulate a local collection, use the operators TABLE and CAST. The operands of CAST are a collection declared locally (in a PL/SQL anonymous block for example) and a SQL collection type. CAST converts the local collection to the specified type. That way, you can manipulate the collection as if it were a SQL database table. In the following example, you count the number of differences between a revised course list and the original (notice that the number of credits for course 3720 changed from 4 to 3): DECLARE revised CourseList := CourseList(Course(1002, "Expository Writing", 3), Course(2020, "Film and Literature", 4), Course(2810, "Discursive Writing", 4), Course(3010, "Modern English Grammar ", 3), Course(3550, "Realism and Naturalism", 4), Course(3720, "Introduction to Shakespeare", 3), Course(3760, "Modern Drama", 4), Course(3822, "The Short Story", 4), Course(3870, "The American Novel", 5), Course(4210, "20th-Century Poetry", 4), Course(4725, "Advanced Workshop in Poetry", 5)); num_changed INTEGER;BEGIN SELECT COUNT(*) INTO num_changed FROM TABLE(CAST(revised AS CourseList)) AS new, TABLE(SELECT courses FROM department WHERE name = "English") AS old WHERE new.course_no = old.course_no AND (new.title != old.title OR new.credits != old.credits); DBMS_OUTPUT.PUT_LINE(num_changed);END; Using Collection MethodsThe following collection methods help generalize code, make collections easier to use, and make your applications easier to maintain: EXISTSCOUNTLIMITFIRST and LASTPRIOR and NEXTEXTENDTRIMDELETEA collection method is a built-in function or procedure that operates on collections and is called using dot notation. The syntax follows: collection_name.method_name<(parameters)> Collection methods cannot be called from SQL statements. Also, EXTEND and TRIM cannot be used with index-by tables. EXISTS, COUNT, LIMIT, FIRST, LAST, PRIOR, and NEXT are functions; EXTEND, TRIM, and DELETE are procedures. EXISTS, PRIOR, NEXT, TRIM, EXTEND, and DELETE take integer parameters.Only EXISTS can be applied to atomically null collections. If you apply another method to such collections, PL/SQL raises COLLECTION_IS_NULL. Using EXISTSEXISTS(n) returns TRUE if the nth element in a collection exists. Otherwise, EXISTS(n) returns FALSE. Mainly, you use EXISTS with DELETE to maintain sparse nested tables. You can also use EXISTS to avoid raising an exception when you reference a nonexistent element. In the following example, PL/SQL executes the assignment statement only if element i exists: IF courses.EXISTS(i) THEN courses(i) := new_course; END IF; When passed an out-of-range subscript, EXISTS returns FALSE instead of raising SUBSCRIPT_OUTSIDE_LIMIT. Using COUNTCOUNT returns the number of elements that a collection currently contains. For instance, if varray projects contains 15 elements, the following IF condition is true: IF projects.COUNT = 25 THEN ... COUNT is useful because the current size of a collection is not always known. For example, if you fetch a column of starrkingschool.net data into a nested table, how many elements does the table contain? COUNT gives you the answer. You can use COUNT wherever an integer expression is allowed. In the next example, you use COUNT to specify the upper bound of a loop range: FOR i IN 1..courses.COUNT LOOP ... For varrays, COUNT always equals LAST. For nested tables, COUNT normally equals LAST. But, if you delete elements from the middle of a nested table, COUNT becomes smaller than LAST. When tallying elements, COUNT ignores deleted elements. Using LIMITFor nested tables, which have no maximum size, LIMIT returns NULL. For varrays, LIMIT returns the maximum number of elements that a varray can contain (which you must specify in its type definition). For instance, if the maximum size of varray projects is 25 elements, the following IF condition is true: IF projects.LIMIT = 25 THEN ... You can use LIMIT wherever an integer expression is allowed. In the following example, you use LIMIT to determine if you can add 15 more elements to varray projects: IF (projects.COUNT + 15) Using FIRST and LASTFIRST and LAST return the first and last (smallest and largest) index numbers in a collection. If the collection is empty, FIRST and LAST return NULL. If the collection contains only one element, FIRST and LAST return the same index number, as the following example shows: IF courses.FIRST = courses.LAST THEN ... -- only one element The next example shows that you can use FIRST and LAST to specify the lower and upper bounds of a loop range provided each element in that range exists: FOR i IN courses.FIRST..courses.LAST LOOP ... In fact, you can use FIRST or LAST wherever an integer expression is allowed. In the following example, you use FIRST to initialize a loop counter: i := courses.FIRST;WHILE i IS NOT NULL LOOP ... For varrays, FIRST always returns 1 and LAST always equals COUNT. For nested tables, FIRST normally returns 1. But, if you delete elements from the beginning of a nested table, FIRST returns a number larger than 1. Also for nested tables, LAST normally equals COUNT. But, if you delete elements from the middle of a nested table, LAST becomes larger than COUNT. When scanning elements, FIRST and LAST ignore deleted elements. Using PRIOR and NEXTPRIOR(n) returns the index number that precedes index n in a collection. NEXT(n) returns the index number that succeeds index n. If n has no predecessor, PRIOR(n) returns NULL. Likewise, if n has no successor, NEXT(n) returns NULL. PRIOR and NEXT do not wrap from one end of a collection to the other. For example, the following statement assigns NULL to n because the first element in a collection has no predecessor: n := courses.PRIOR(courses.FIRST); -- assigns NULL to n PRIOR is the inverse of NEXT. For instance, if element i exists, the following statement assigns element i to itself: projects(i) := projects.PRIOR(projects.NEXT(i)); You can use PRIOR or NEXT to traverse collections indexed by any series of subscripts. In the following example, you use NEXT to traverse a nested table from which some elements have been deleted: i := courses.FIRST; -- get subscript of first elementWHILE i IS NOT NULL LOOP -- do something with courses(i) i := courses.NEXT(i); -- get subscript of next elementEND LOOP; When traversing elements, PRIOR and NEXT ignore deleted elements. Using EXTENDTo increase the size of a collection, use EXTEND. (You cannot use EXTEND with index-by tables.) This procedure has three forms. EXTEND appends one null element to a collection. EXTEND(n) appends n null elements to a collection. EXTEND(n,i) appends n copies of the ith element to a collection. For example, the following statement appends 5 copies of element 1 to nested table courses: courses.EXTEND(5,1); You cannot use EXTEND to initialize an atomically null collection. Also, if you impose the NOT NULL constraint on a TABLE or VARRAY type, you cannot apply the first two forms of EXTEND to collections of that type. EXTEND operates on the internal size of a collection, which includes any deleted elements. So, if EXTEND encounters deleted elements, it includes them in its tally. PL/SQL keeps placeholders for deleted elements so that you can replace them if you wish. Consider the following example: DECLARE TYPE CourseList IS TABLE OF VARCHAR2(10); courses CourseList;BEGIN courses := CourseList("Biol 4412", "Psyc 3112", "Anth 3001"); courses.DELETE(3); -- delete element 3 /* PL/SQL keeps a placeholder for element 3. So, the next statement appends element 4, not element 3. */ courses.EXTEND; -- append one null element /* Now element 4 exists, so the next statement doens not raise SUBSCRIPT_BEYOND_COUNT. */ courses(4) := "Engl 2005"; When it includes deleted elements, the internal size of a nested table differs from the values returned by COUNT and LAST. For instance, if you initialize a nested table with five elements, then delete elements 2 and 5, the internal size is 5, COUNT returns 3, and LAST returns 4. All deleted elements (whether leading, in the middle, or trailing) are treated alike. Using TRIMThis procedure has two forms. TRIM removes one element from the end of a collection. TRIM(n) removes n elements from the end of a collection. For example, this statement removes the last three elements from nested table courses: courses.TRIM(3); If n is greater than COUNT, TRIM(n) raises SUBSCRIPT_BEYOND_COUNT. TRIM operates on the internal size of a collection. So, if TRIM encounters deleted elements, it includes them in its tally. Consider the following example: DECLARE TYPE CourseList IS TABLE OF VARCHAR2(10); courses CourseList;BEGIN courses := CourseList("Biol 4412", "Psyc 3112", "Anth 3001"); courses.DELETE(courses.LAST); -- delete element 3 /* At this point, COUNT equals 2, the number of valid elements remaining. So, you might expect the following statement to empty the nested table by trimminns elements 1 and 2. Instead, it trims valid element 2 and deleted element 3 because TRIM includes turned off elements in its tally. */ courses.TRIM(courses.COUNT); DBMS_OUTPUT.PUT_LINE(courses(1)); -- prints "Biol 4412" In general, do not depend on the interaction between TRIM and DELETE. It is better to treat nested tables like fixed-size arrays and use only DELETE, or to treat them like stacks and use only TRIM and EXTEND. PL/SQL does not keep placeholders for trimmed elements. So, you cannot replace a trimmed element simply by assigning it a new value. Using DELETEThis procedure has three forms. DELETE removes all elements from a collection. DELETE(n) removes the nth element from an index-by table or nested table. If n is null, DELETE(n) does nothing. DELETE(m,n) removes all elements in the range m..n from an index-by table or nested table. If m is larger than n or if m or n is null, DELETE(m,n) does nothing. Some examples follow: BEGIN ... courses.DELETE(2); -- deletes element 2 courses.DELETE(7,7); -- deletes element 7 courses.DELETE(6,3); -- does nopoint courses.DELETE(3,6); -- deletes elements 3 through 6 projects.DELETE; -- deletes all elements END; Varrays are dense, so you cannot delete their individual elements. 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 simply by assigning it a new value.DELETE allows you to maintain sparse nested tables. In the following example, you retrieve nested table prospects into a temporary table, prune it, then store it back in the database: DECLARE my_prospects ProspectList; revenue NUMBER;BEGIN SELECT prospects INTO my_prospects FROM customers WHERE ... FOR i IN my_prospects.FIRST..my_prospects.LAST LOOP estimate_revenue(my_prospects(i), revenue); -- call procedure IF revenue my_prospects.DELETE(i); END IF; END LOOP; UPDATE customers SET prospects = my_prospects WHERE ... 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 ParametersWithin a subprogram, a collection parameter assumes the properties of the argument bound to it. So, you can apply the built-in collection methods (FIRST, LAST, COUNT, and so on) to such parameters. In the following example, a nested table is declared as the formal parameter of a packaged procedure: CREATE PACKAGE personnel AS TYPE Staff IS TABLE OF Employee; ... PROCEDURE award_bonuses (members IN Staff);END personnel;CREATE PACKAGE BODY personnel AS ... PROCEDURE award_bonuses (members IN Staff) IS BEGIN ... IF members.COUNT > 10 THEN -- apply technique ... END IF; END;END personnel; Note: For varray parameters, the value of LIMIT is always derived from the parameter type definition, regardless of the parameter mode. Avoiding Collection ExceptionsIn 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 nullBEGIN /* Assume execution continues despite the raised exceptions. */ nums(1) := 1; -- raises COLLECTION_IS_NULL (1) nums := NumList(1,2); -- initialize table nums(NULL) := 3 -- raises VALUE_ERROR (2) nums(0) := 3; -- raises SUBSCRIPT_OUTSIDE_LIMIT (3) nums(3) := 3; -- raises SUBSCRIPT_BEYOND_COUNT (4) nums.DELETE(1); -- delete element 1 IF nums(1) = 1 THEN ... -- 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 legal 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: ExceptionRaised when ...COLLECTION_IS_NULLyou try to operate on an atomically null collectionNO_DATA_FOUNDa subscript designates an element that was deletedSUBSCRIPT_BEYOND_COUNTa subscript exceeds the number of elements in a collectionSUBSCRIPT_OUTSIDE_LIMITa subscript is outside the legal rangeVALUE_ERRORa subscript is null or not convertible to an integer 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. Also, you can replace deleted elements without raising NO_DATA_FOUND, as the following example shows: DECLARE TYPE NumList IS TABLE OF NUMBER; nums NumList := NumList(10,20,30); -- initialize tableBEGIN ... 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; -- legal; does not raise NO_DATA_FOUND DBMS_OUTPUT.PUT_LINE(nums.COUNT); -- prints 3END; Packaged collection types and local collection types are never compatible. For example, suppose you want to call the following packaged procedure: CREATE PACKAGE pkg1 AS TYPE NumList IS VARRAY(25) OF NUMBER(4); PROCEDURE delete_emps (emp_list NumList); ...END pkg1;CREATE PACKAGE BODY pkg1 AS PROCEDURE delete_emps (emp_list NumList) IS ... ...END pkg1; When you run the PL/SQL block below, the second procedure call fails with a wrong number or types of arguments error. That is because the packaged and local VARRAY types are incompatible even though their definitions are identical.


You are watching: A collection of records is called


See more: How Many Calories In A Half Cup Of Sugar And Nutrition Facts

DECLARE TYPE NumList IS VARRAY(25) OF NUMBER(4); emps pkg1.NumList := pkg1.NumList(7369, 7499); emps2 NumList := NumList(7521, 7566);BEGIN pkg1.delete_emps(emps); pkg1.delete_emps(emps2); -- causes a compilation errorEND; Taking Advantage of Bulk BindsEmbedded in the starrkingschool.net RDBMS, the PL/SQL engine accepts any valid PL/SQL block or subprogram. As Figure4-3 shows, the PL/SQL engine executes procedural statements but sends SQL statements to the SQL engine, which executes the SQL statements and, in some cases, returns data to the PL/SQL engine.Figure 4-3 Context Switching
*