- PVSM.RU - https://www.pvsm.ru -
Статья имеет довольно таки тезисный стиль. Более подробное содержание можно найти в приложенном внизу статьи видео с записью лекции по коллекциям Oracle.
Коллекции присутствую в том или ином виде в большинстве языков программирования и везде имеют схожую суть в плане использования. А именно – позволяют хранить набор объектов одного типа и проводить над всем набором какие-либо действия, либо в цикле проводить однотипные действия со всеми элементами набора.
Таким же образом коллекции используются и в Oracle.

variable_name(index)
| Тип коллекции | Количество элементов | Тип индекса | Плотная или разряженная | Без инициализации | Где объявляется | Использование в SQL |
|---|---|---|---|---|---|---|
| Ассоциативный массив (index by table) |
Не задано | String Pls_integer |
Плотная и разряженная | Empty | PL/SQL block Package |
Нет |
| Varray (variable-size array) |
Задано | Integer | Только плотная | Null | PL/SQL block Package Schema level |
Только определенные на уровне схемы |
| Nested table | Не задано | Integer | При создании плотная, может стать разряженной | Null | PL/SQL block Package Schema level |
Только определенные на уровне схемы |
Плотность коллекции означает, что между элементами коллекции нет пропусков, пустых мест. Некоторые коллекции, как видно из таблицы, могут быть разряженными – т.е. могут иметь разрывы между элементами. Это значит, что в коллекции, например, могут быть элементы с индексом 1 и 4, а с индексом 2 и 3 элементов нет. При этом слоты памяти под 2-й и 3-й элементы будут существовать и будут принадлежать коллекции (в случае nested table), но не содержать при этом объектов и попытка прочитать содержимое этих элементов вызовет ошибку no_data_found.
Подробности можно узнать из видео-лекции в конце статьи.
Также его называют index by table или pl/sql table.
Тип описывается следующим образом (assoc_array_type_def):.

Используются для:
Restrictions:
При изменении параметров NLS_SORT и NLS_COMP во время сессии после заполнения ассоциативного массива, можем получать неожиданные результаты вызовов методов first, last, next, previous. Также могут возникнуть проблемы при передаче ассоциативного массива в качестве параметра на другую БД с иными настройками NLS_SORT и NLS_COMP
Представляет собой массив последовательно хранящихся элементов

Тип описывается следующим образом (varay_type_def):

collection_type ( [ value [, value ]... ] )
Используется, если:
Restrictions:
Максимальный размер – 2 147 483 647 элементов
Тип описывается следующим образом (nested_table_type_def):


collection_type ( [ value [, value ]... ] )
SELECT column_value FROM TABLE(nested_table)
Операции возможны только с коллекциями nested table. Обе коллекции, участвующие в операции, должны быть одного типа.
Результатом операции также является коллекция nested table.
| Операция | Описание |
|---|---|
| MULTISET UNION | Возвращает объединение двух коллекций |
| MULTISET UNION DISTINCT | Возвращает объединение двух коллекций с дистинктом (убирает дубли) |
| MULTISET INTERSECT | Возвращает пересечение двух коллекций |
| MULTISET INTERSECT DISTINCT | Возвращает пересечение двух коллекций с дистинктом (убирает дубли) |
| SET | Возвращает коллекцию с дистинктом (т.е. коллекцию без дублей) |
| MULTISET EXCEPT | Возвращает разницу двух коллекций |
| MULTISET EXCEPT DISTINCT | Возвращает разницу двух коллекций с дистинктом (убирает дубли) |
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;
Результат:
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
| Операция | Описание |
|---|---|
| IS NULL (IS NOT NULL) | Сравнивает коллекцию со значением NULL |
| Сравнение = | Две коллекции nested table можно сравнить, если они одного типа и не содержат записей типа record. Они равны, если имеют одинаковые наборы элементов (не зависимо от порядка хранения элементов внутри коллекции) |
| IN | Сравнивает коллекцию с перечисленными в скобках |
| SUBMULTISET OF | Проверяет, является ли коллекция подмножеством другой коллекции |
| MEMBER OF | Проверяет, является ли конкретный элемент(объект) частью коллекции |
| IS A SET | Проверяет, содержит ли коллекция дубли |
| IS EMPTY | Проверяет, пуста ли коллекция |
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;
Результат:
nt1 = nt2
nt1 IN (nt2,nt3,nt4)
nt1 SUBMULTISET OF nt3
3 MEMBER OF nt3
nt3 IS NOT A SET
nt4 IS EMPTY
Синтаксис вызова методов:
collection_name.method
| Метод | Тип | Описание | Index by table | Varray | Nested table |
|---|---|---|---|---|---|
| DELETE | Процедура | Удаляет элементы из коллекции | Да | Нет | Да |
| TRIM | Процедура | Удаляет элементы с конца коллекции (работает с внутренним размером коллекции) | Нет | Да | Да |
| EXTEND | Процедура | Добавляет элементы в конец коллекции | Нет | Да | Да |
| EXISTS | Функция | Возвращает TRUE, если элемент присутствует в коллекции | Да | Да | Да |
| FIRST | Функция | Возвращает первый индекс коллекции | Да | Да | Да |
| LAST | Функция | Возвращает последний индекс коллекции | Да | Да | Да |
| COUNT | Функция | Возвращает количество элементов в коллекции | Да | Да | Да |
| LIMIT | Функция | Возвращает максимальное количество элементов, которые может хранить коллекция | Нет | Да | Нет |
| PRIOR | Функция | Возвращает индекс предыдущего элемента коллекции | Да | Да | Да |
| NEXT | Функция | Возвращает индекс следующего элемента коллекции | Да | Да | Да |
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;
Результаты:
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
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;
Результат:
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
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;
Результат:
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
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;
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;
Результат:
Before deletions:
FIRST = A
LAST = Z
After deletions:
FIRST = K
LAST = R
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;
Результат
n.COUNT = 4, n.LAST = 4
n.COUNT = 7, n.LAST = 7
n.COUNT = 2, n.LAST = 2
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;
Результат:
aa.COUNT = 4
aa.LIMIT =
va.COUNT = 2
va.LIMIT = 4
nt.COUNT = 3
nt.LIMIT =
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;
Результат:
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) =
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 i IN INDICES OF cust_tab
(конструкция не работает для ассоциативных массивов, индексированных строками)
FORALL i IN VALUES OF rejected_order_tab
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;
FORALL j IN collection.FIRST.. collection.LAST SAVE EXCEPTIONS
Генерит ORA-24381 в конце, если в цикле возникали исключения
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;
В каких случаях нужно освобождать память:
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;
Видео-запись лекции, по материалам которой и была написана эта статья:
Автор: MoscowDevelopmentTeam
Источник [22]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/oracle/87314
Ссылки в тексте:
[1] Общие сведения о коллекциях в pl/sql : #One
[2] Типы коллекций : #Two
[3] Ассоциативный массив : #Three
[4] Varray : #Four
[5] Nested table : #Five
[6] Set operations с nested tables : #Six
[7] Логические операции с коллекциями : #Seven
[8] Методы коллекций : #Eight
[9] Delete : #Nine
[10] Trim : #Ten
[11] Extend : #Eleven
[12] Exists : #Twelve
[13] First и Last : #Thirteen
[14] Count: #Fourteen
[15] Limit : #Fifteen
[16] Prior и Next : #Sixteen
[17] Bulk Collect : #Seventeen
[18] Цикл forall: #Eighteen
[19] Exceptions in forall : #Nineteen
[20] Collection exceptions : #Twenty
[21] DBMS_SESSION.FREE_UNUSED_USER_MEMORY: #TwentyOne
[22] Источник: http://habrahabr.ru/post/254355/
Нажмите здесь для печати.