📜 ⬆️ ⬇️

All about collections in Oracle

The article has a pretty thesis style. More detailed content can be found in the video attached at the bottom of the article recording a lecture on Oracle collections.

Collections are present in one form or another in most programming languages ​​and everywhere have a similar essence in terms of use. Namely, they allow you to store a set of objects of the same type and to carry out any actions over the entire set, or to carry out similar actions with all elements of the set in a loop.

In the same way, collections are used in Oracle.
')

The content of the article




Pl / sql Collections Overview



Types of collections

Collection typeAmount of elementsIndex typeDense or sparseWithout initializationWhere is announcedUse in SQL
Associative array
(index by table)
Not setString
Pls_integer
Dense and sparseEmptyPL / SQL block
Package
Not
Varray
(variable-size array)
SetIntegerOnly denseNullPL / SQL block
Package
Schema level
Only defined at the schema level
Nested tableNot setIntegerWhen you create a dense, may become rarefiedNullPL / SQL block
Package
Schema level
Only defined at the schema level

The density of the collection means that there are no gaps, empty spaces between the elements of the collection. Some collections, as can be seen from the table, can be sparse — that is, may have gaps between elements. This means that in the collection, for example, there may be elements with index 1 and 4, and with index 2 and 3 there are no elements. In this case, the memory slots for the 2nd and 3rd elements will exist and will belong to the collection (in the case of nested table), but not contain objects and an attempt to read the contents of these elements will cause an error no_data_found.
Details can be found in the video lecture at the end of the article.

Associative array


Also called index by table or pl / sql table.
The type is described as follows (assoc_array_type_def) :.



Used for:


Restrictions:
When changing the NLS_SORT and NLS_COMP parameters during a session after filling in the associative array, we can get unexpected results of calls to the methods first, last, next, previous. There may also be problems when passing an associative array as a parameter to another database with different settings NLS_SORT and NLS_COMP

Varray

It is an array of sequentially stored items.


The type is described as follows (varay_type_def):




Used if:


Restrictions:
The maximum size is 2,147,483,647 items

Nested table

The type is described as follows (nested_table_type_def):




Set operations with nested tables

Operations are possible only with nested table collections. Both collections involved in the operation must be of the same type.
The result of the operation is also a collection of nested table .

OperationDescription
MULTISET UNIONReturns the union of two collections
MULTISET UNION DISTINCTReturns the union of two collections with distinction (removes doubles)
MULTISET INTERSECTReturns the intersection of two collections.
MULTISET INTERSECT DISTINCTReturns the intersection of two collections with distinction (removes doubles)
SETReturns a collection with distinction (i.e., a collection without duplicates)
MULTISET EXCEPTReturns the difference of two collections.
MULTISET EXCEPT DISTINCTReturns the difference of two collections with distinction (removes doubles)

Small example
A small example (note the result of the MULTISET EXCEPT DISTINCT operation)
 DECLARE TYPE nested_typ IS TABLE OF NUMBER; nt1 nested_typ := nested_typ(1,2,3); nt2 nested_typ := nested_typ(3,2,1); nt3 nested_typ := nested_typ(2,3,1,3); nt4 nested_typ := nested_typ(1,2,4); answer nested_typ; BEGIN answer := nt1 MULTISET UNION nt4; answer := nt1 MULTISET UNION nt3; answer := nt1 MULTISET UNION DISTINCT nt3; answer := nt2 MULTISET INTERSECT nt3; answer := nt2 MULTISET INTERSECT DISTINCT nt3; answer := SET(nt3); answer := nt3 MULTISET EXCEPT nt2; answer := nt3 MULTISET EXCEPT DISTINCT nt2; END; 


Result:
 nt1 MULTISET UNION nt4: 1 2 3 1 2 4 nt1 MULTISET UNION nt3: 1 2 3 2 3 1 3 nt1 MULTISET UNION DISTINCT nt3: 1 2 3 nt2 MULTISET INTERSECT nt3: 3 2 1 nt2 MULTISET INTERSECT DISTINCT nt3: 3 2 1 SET(nt3): 2 3 1 nt3 MULTISET EXCEPT nt2: 3 nt3 MULTISET EXCEPT DISTINCT nt2: empty set 



Logical operations with collections

OperationDescription
IS NULL (IS NOT NULL)Compares the collection with a NULL value.
Comparison =Two nested table collections can be compared if they are of the same type and do not contain records of the record type. They are equal if they have the same sets of elements (regardless of the order in which the elements inside the collection are stored)
INCompares the collection with those listed in brackets.
SUBMULTISET OFChecks if a collection is a subset of another collection.
MEMBER OFChecks if a particular item (object) is part of a collection.
IS A SETChecks if the collection contains duplicates
IS EMPTYChecks if the collection is empty


A small example of the use of logical operations with collections
 DECLARE TYPE nested_typ IS TABLE OF NUMBER; nt1 nested_typ := nested_typ(1, 2, 3); nt2 nested_typ := nested_typ(3, 2, 1); nt3 nested_typ := nested_typ(2, 3, 1, 3); nt4 nested_typ := nested_typ(); BEGIN IF nt1 = nt2 THEN DBMS_OUTPUT.PUT_LINE('nt1 = nt2'); END IF; IF (nt1 IN (nt2, nt3, nt4)) THEN DBMS_OUTPUT.PUT_LINE('nt1 IN (nt2,nt3,nt4)'); END IF; IF (nt1 SUBMULTISET OF nt3) THEN DBMS_OUTPUT.PUT_LINE('nt1 SUBMULTISET OF nt3'); END IF; IF (3 MEMBER OF nt3) THEN DBMS_OUTPUT.PUT_LINE('3 MEMBER OF nt3'); END IF; IF (nt3 IS NOT A SET) THEN DBMS_OUTPUT.PUT_LINE('nt3 IS NOT A SET'); END IF; IF (nt4 IS EMPTY) THEN DBMS_OUTPUT.PUT_LINE('nt4 IS EMPTY'); END IF; END; 


Result:
 nt1 = nt2 nt1 IN (nt2,nt3,nt4) nt1 SUBMULTISET OF nt3 3 MEMBER OF nt3 nt3 IS NOT A SET nt4 IS EMPTY 



Collection methods

Method call syntax:
 collection_name.method 

MethodType ofDescriptionIndex by tableVarrayNested table
DELETEProcedureRemoves items from the collection.YesOnly version without parametersYes
TRIMProcedureRemoves items from the end of the collection (works with the internal size of the collection)NotYesYes
EXTENDProcedureAdds items to the end of the collection.NotYesYes
EXISTSFunctionReturns TRUE if the item is in the collection.YesYesYes
FIRSTFunctionReturns the first index in the collection.YesYesYes
LASTFunctionReturns the last index in the collection.YesYesYes
COUNTFunctionReturns the number of items in the collection.YesYesYes
LIMITFunctionReturns the maximum number of items the collection can store.NotYesNot
PRIORFunctionReturns the index of the previous item in the collection.YesYesYes
NEXTFunctionReturns the index of the next item in the collection.YesYesYes


Delete
  • Delete deletes all items. Immediately clears the memory allocated to store these items.
  • Delete (n) deletes the element with index n. Memory does not free. The element can be restored (ie, set a new one) and it will occupy the same memory as the previous one.
  • Delete (n, m) deletes items with indices in the interval n..m
  • If there is no item to delete in the collection, it does nothing.
  • For varray collections, only the version of the method is available without parameters.


Usage example
 DECLARE TYPE nt_type IS TABLE OF NUMBER; nt nt_type := nt_type(11, 22, 33, 44, 55, 66); BEGIN nt.DELETE(2); --    nt(2) := 2222; --  2-  nt.DELETE(2, 4); --    2-  4- nt(3) := 3333; --  3-  nt.DELETE; --    END; 

Results:
 beginning: 11 22 33 44 55 66 after delete(2): 11 33 44 55 66 after nt(2) := 2222: 11 2222 33 44 55 66 after delete(2, 4): 11 55 66 after nt(3) := 3333: 11 3333 55 66 after delete: empty set 



Trim



Usage example
 DECLARE TYPE nt_type IS TABLE OF NUMBER; nt nt_type := nt_type(11, 22, 33, 44, 55, 66); BEGIN nt.TRIM; -- Trim last element nt.DELETE(4); -- Delete fourth element nt.TRIM(2); -- Trim last two elements END; 

Result:
 beginning: 11 22 33 44 55 66 after TRIM: 11 22 33 44 55 after DELETE(4): 11 22 33 55 after TRIM(2): 11 22 33 



Extend



Usage example
 DECLARE TYPE nt_type IS TABLE OF NUMBER; nt nt_type := nt_type(11, 22, 33); BEGIN nt.EXTEND(2, 1); -- Append two copies of first element nt.DELETE(5); -- Delete fifth element nt.EXTEND; -- Append one null element END; 

Result:
 beginning: 11 22 33 after EXTEND(2,1): 11 22 33 11 11 after DELETE(5): 11 22 33 11 after EXTEND: 11 22 33 11 



Exists


Usage example
 DECLARE TYPE NumList IS TABLE OF INTEGER; n NumList := NumList(1, 3, 5, 7); BEGIN n.DELETE(2); -- Delete second element FOR i IN 1 .. 6 LOOP IF n.EXISTS(i) THEN DBMS_OUTPUT.PUT_LINE('n('||i||') = ' || n(i)); ELSE DBMS_OUTPUT.PUT_LINE('n('||i||') does not exist'); END IF; END LOOP; END; 



First and Last


Usage example
 DECLARE TYPE aa_type_str IS TABLE OF INTEGER INDEX BY VARCHAR2(10); aa_str aa_type_str; BEGIN aa_str('Z') := 26; aa_str('A') := 1; aa_str('K') := 11; aa_str('R') := 18; DBMS_OUTPUT.PUT_LINE('Before deletions:'); DBMS_OUTPUT.PUT_LINE('FIRST = ' || aa_str.FIRST); DBMS_OUTPUT.PUT_LINE('LAST = ' || aa_str.LAST); aa_str.DELETE('A'); aa_str.DELETE('Z'); DBMS_OUTPUT.PUT_LINE('After deletions:'); DBMS_OUTPUT.PUT_LINE('FIRST = ' || aa_str.FIRST); DBMS_OUTPUT.PUT_LINE('LAST = ' || aa_str.LAST); END; 

Result:
 Before deletions: FIRST = A LAST = Z After deletions: FIRST = K LAST = R 



Count

Usage example
 DECLARE TYPE NumList IS VARRAY(10) OF INTEGER; n NumList := NumList(1, 3, 5, 7); BEGIN DBMS_OUTPUT.PUT('n.COUNT = ' || n.COUNT || ', '); DBMS_OUTPUT.PUT_LINE('n.LAST = ' || n.LAST); n.EXTEND(3); DBMS_OUTPUT.PUT('n.COUNT = ' || n.COUNT || ', '); DBMS_OUTPUT.PUT_LINE('n.LAST = ' || n.LAST); n.TRIM(5); DBMS_OUTPUT.PUT('n.COUNT = ' || n.COUNT || ', '); DBMS_OUTPUT.PUT_LINE('n.LAST = ' || n.LAST); END; 

Result
 n.COUNT = 4, n.LAST = 4 n.COUNT = 7, n.LAST = 7 n.COUNT = 2, n.LAST = 2 



Limit


Usage example
 DECLARE TYPE aa_type IS TABLE OF INTEGER INDEX BY PLS_INTEGER; aa aa_type; -- associative array TYPE va_type IS VARRAY(4) OF INTEGER; va va_type := va_type(2, 4); -- varray TYPE nt_type IS TABLE OF INTEGER; nt nt_type := nt_type(1, 3, 5); -- nested table BEGIN aa(1) := 3; aa(2) := 6; aa(3) := 9; aa(4) := 12; DBMS_OUTPUT.PUT_LINE('aa.COUNT = ' || aa.count); DBMS_OUTPUT.PUT_LINE('aa.LIMIT = ' || aa.limit); DBMS_OUTPUT.PUT_LINE('va.COUNT = ' || va.count); DBMS_OUTPUT.PUT_LINE('va.LIMIT = ' || va.limit); DBMS_OUTPUT.PUT_LINE('nt.COUNT = ' || nt.count); DBMS_OUTPUT.PUT_LINE('nt.LIMIT = ' || nt.limit); END; 

Result:
 aa.COUNT = 4 aa.LIMIT = va.COUNT = 2 va.LIMIT = 4 nt.COUNT = 3 nt.LIMIT = 



Prior and Next


Usage example
 DECLARE TYPE nt_type IS TABLE OF NUMBER; nt nt_type := nt_type(18, NULL, 36, 45, 54, 63); BEGIN nt.DELETE(4); DBMS_OUTPUT.PUT_LINE('nt(4) was deleted.'); FOR i IN 1 .. 7 LOOP DBMS_OUTPUT.PUT('nt.PRIOR(' || i || ') = '); print(nt.PRIOR(i)); DBMS_OUTPUT.PUT('nt.NEXT(' || i || ') = '); print(nt.NEXT(i)); END LOOP; END; 

Result:
 nt(4) was deleted. nt.PRIOR(1) = nt.NEXT(1) = 2 nt.PRIOR(2) = 1 nt.NEXT(2) = 3 nt.PRIOR(3) = 2 nt.NEXT(3) = 5 nt.PRIOR(4) = 3 nt.NEXT(4) = 5 nt.PRIOR(5) = 3 nt.NEXT(5) = 6 nt.PRIOR(6) = 5 nt.NEXT(6) = nt.PRIOR(7) = 6 nt.NEXT(7) = 



Bulk collect



Usage example
 DECLARE TYPE NumTab IS TABLE OF employees.employee_id%TYPE; TYPE NameTab IS TABLE OF employees.last_name%TYPE; CURSOR c1 IS SELECT employee_id,last_name FROM employees WHERE salary > 10000 ORDER BY last_name; enums NumTab; names NameTab; BEGIN SELECT employee_id, last_name BULK COLLECT INTO enums, names FROM employees ORDER BY employee_id; OPEN c1; LOOP FETCH c1 BULK COLLECT INTO enums, names LIMIT 10; EXIT WHEN names.COUNT = 0; do_something(); END LOOP; CLOSE c1; DELETE FROM emp_temp WHERE department_id = 30 RETURNING employee_id, last_name BULK COLLECT INTO enums, names; END; 



Forall cycle





Usage example
 DECLARE TYPE NumList IS TABLE OF NUMBER; depts NumList := NumList(10, 20, 30); TYPE enum_t IS TABLE OF employees.employee_id%TYPE; e_ids enum_t; TYPE dept_t IS TABLE OF employees.department_id%TYPE; d_ids dept_t; BEGIN FORALL j IN depts.FIRST .. depts.LAST DELETE FROM emp_temp WHERE department_id = depts(j) RETURNING employee_id, department_id BULK COLLECT INTO e_ids, d_ids; END; 



Exceptions in forall



Collection exceptions



Examples of situations that throw exceptions
 DECLARE TYPE NumList IS TABLE OF NUMBER; nums NumList; BEGIN nums(1) := 1; -- raises COLLECTION_IS_NULL nums := NumList(1, 2); nums(NULL) := 3; -- raises VALUE_ERROR nums(0) := 3; -- raises SUBSCRIPT_BEYOND_COUNT nums(3) := 3; --raises SUBSCRIPT_OUTSIDE_LIMIT nums.Delete(1); IF nums(1) = 1 THEN ... -- raises NO_DATA_FOUND END; 



DBMS_SESSION.FREE_UNUSED_USER_MEMORY




When it is necessary to free memory:


Usage example
 CREATE PACKAGE foobar type number_idx_tbl is table of number indexed by binary_integer; store1_table number_idx_tbl; -- PL/SQL indexed table store2_table number_idx_tbl; -- PL/SQL indexed table store3_table number_idx_tbl; -- PL/SQL indexed table ... END; -- end of foobar DECLARE ... empty_table number_idx_tbl; -- uninitialized ("empty") version BEGIN FOR i in 1..1000000 loop store1_table(i) := i; -- load data END LOOP; ... store1_table := empty_table; -- "truncate" the indexed table ... - dbms_session.free_unused_user_memory; -- give memory back to system store1_table(1) := 100; -- index tables still declared; store2_table(2) := 200; -- but truncated. ... END; 



A video recording of the lecture, based on which this article was written:



A variety of other Oracle-related videos can be found on this channel:
www.youtube.com/c/MoscowDevelopmentTeam

Other Oracle Articles


All about triggers in Oracle

Source: https://habr.com/ru/post/254355/


All Articles