Встроенные методы коллекций в PL/SQL Oracle
PL/SQL предоставляет для коллекций множество встроенных функций и процедур, называемых методами коллекций. Эти методы предназначены для получения информации о содержимом коллекции и ее изменения.
- COUNT (функция) — Возвращает текущее значение элементов в коллекции;
- DELETE (процедура) — Удаляет из коллекции один или несколько элементов. Уменьшает значение, возвращаемое функцией COUNT, если заданные элементы еще не удалены. Со структурами VARRAY может использоваться только для удаления всего содержимого;
- EXISTS (функция) — Возвращает значение TRUE или FALSE, определяющее, существует ли в коллекции заданный элемент;
- EXTEND (процедура) — Увеличивает количество элементов во вложенной таблице или VARRAY, а также значение, возвращаемое функцией COUNT;
- FIRST, LAST (функции) — Возвращают индексы первого (FIRST) и последнего (LAST) элемента в коллекции;
- LIMIT (функция) — Возвращает максимальное количество элементов в массиве VARRAY;
- PRIOR, NEXT (функции) — Возвращают индексы элементов, предшествующих заданному (PRIOR) и следующему за ним (NEXT). Всегда используйте PRIOR и NEXT для перебора коллекций, особенно при работе с разреженными (или потенциально разреженными) коллекциями;
- TRIM (функция) — Удаляет элементы, начиная с конца коллекции (элемент с наибольшим индексом);
Все эти конструкции называются методами, потому что синтаксис их вызова отличается от синтаксиса вызова обычных процедур и функций. Это типичный синтаксис вызова методов, используемый в объектно-ориентированных языках программирования — таких, как Java.
В общем случае синтаксис вызова методов ассоциативного массива выглядит так:
Операция, не требующая передачи аргументов:
имя_таблицы.операция
Операция, аргументами которой являются индексы элементов:
имя_таблицы.операция(индекс [, индекс])
Методы коллекций недоступны из SQL, их можно использовать только в программах PL/SQL.
Метод COUNT
Метод COUNT возвращает количество элементов в ассоциативном массиве, вложенной таблице или массиве VARRAY. В значении не учитываются элементы, удаленные из коллекции методом DELETE или TRIM.
Синтаксис метода COUNT:
FUNCTION COUNT RETURN PLS_INTEGER; Рассмотрим пример. Прежде чем что-либо делать с коллекцией, мы проверяем, содержит ли она хотя бы один элемент: DECLARE volunteer_list volunteer_list_ar := volunteer_list_ar('Steven'); BEGIN IF volunteer_list.COUNT > 0 THEN assign_tasks (volunteer_list); END IF; END;
Граничные условия
Для инициализированной коллекции, не содержащей ни одного элемента, COUNT возвращает нуль. Это же значение возвращается при вызове COUNT для пустого ассоциативного массива.
Возможные исключения
При вызове метода COUNT для неинициализированной вложенной таблицы или VARRAY инициируется заранее определенное исключение COLLECTION_IS_NULL. Такое исключение не может возникнуть при работе с ассоциативными массивами, не требующими инициализации.
Метод DELETE
Метод DELETE предназначен для удаления одного, нескольких или всех элементов ассоциативного массива, вложенной таблицы или массива VARRAY. При вызове без аргументов он удаляет все элементы коллекции. Вызов DELETE(i) удаляет i-й элемент вложенной таблицы или ассоциативного массива. А вызов DELETE(i,j) удаляет все элементы с индексами от i до j включительно. Если коллекция представляет собой ассоциативный массив, индексируемый строками, i и j должны быть строковыми значениями, в противном случае они являются целыми числами.
При вызове с аргументами метод резервирует место, занимавшееся «удаленным» элементом, и позднее этому элементу можно присвоить новое значение.
Фактически PL/SQL освобождает память лишь при условии, что программа удаляет количество элементов, достаточное для освобождения целой страницы памяти. (Если же метод DELETE вызывается без параметров и очищает всю коллекцию, память освобождается немедленно.)
Применительно к массивам VARRAY метод DELETE может вызываться только без аргументов. Иначе говоря, с помощью указанного метода из этой структуры нельзя удалять отдельные элементы, поскольку в таком случае она станет разреженной, что недопустимо. Единственный способ удалить из VARRAY один или несколько элементов — воспользоваться методом TRIM, предназначенным для удаления группы расположенных рядом элементов, начиная с конца коллекции.
Следующая процедура удаляет из коллекции все элементы, кроме последнего. В ней используются четыре метода:
- FIRST — для получения номера первого удаляемого элемента;
- LAST — для получения номера последнего удаляемого элемента;
- PRIOR — для определения номера предпоследнего элемента;
- DELETE — для удаления всех элементов, кроме последнего
PROCEDURE keep_last (the_list IN OUT List_t) AS first_elt PLS_INTEGER := the_list.FIRST; next_to_last_elt PLS_INTEGER := the_list.PRIOR(the_list.LAST); BEGIN the_list.DELETE(first_elt, next_to_last_elt); END;
Несколько дополнительных примеров:
Удаление всех строк из таблицы names:
names.DELETE;
Удаление 77-й строки из таблицы globals:
globals.DELETE (77);
Удаление из таблицы temp_reading всех элементов, начиная с индекса –15 000 и до индекса 0 включительно:
temp_readings.DELETE (-15000, 0);
Граничные условия
Если значения индексов i и/или j указывают на несуществующие элементы, DELETE пытается «сделать наилучшее» и не генерирует исключение. Например, если таблица содержит три элемента с индексами 1, 2 и 3, то вызов метода DELETE(–5,1) удалит только один элемент с индексом 1, а вызов DELETE(–5) не изменит состояния коллекции.
Возможные исключения
Вызов метода DELETE для неинициализированной вложенной таблицы или массива VARRAY инициирует исключение COLLECTION_IS_NULL.
Метод EXISTS
Метод EXISTS используется с вложенными таблицами, ассоциативными массивами и массивами VARRAY для определения наличия в коллекции заданного элемента. Если таковой имеется, метод возвращает значение TRUE, а если отсутствует — значение FALSE. Значение NULL не возвращается ни при каких условиях. Кроме того, EXISTS возвращает FALSE и в том случае, если заданный элемент был удален из коллекции с помощью метода TRIM или DELETE. Следующий блок проверяет, присутствует ли заданный элемент в коллекции, и при положительном ответе присваивает ему значение NULL:
DECLARE my_list color_tab_t := color_tab_t(); element PLS_INTEGER := 1; BEGIN ... IF my_list.EXISTS(element) THEN my_list(element) := NULL; END IF; END;
Граничные условия
Если метод EXISTS вызывается для неинициализированной (содержащей атомарное значение NULL) вложенной таблицы или структуры VARRAY либо для инициализированной коллекции, не содержащей ни одного элемента, он просто возвращает значение FALSE. Поэтому его можно вызывать без предварительной проверки вызовом COUNT, не рискуя получить сообщение об ошибке.
Возможные исключения
Метод EXISTS не инициирует исключения.
Метод EXTEND
Чтобы добавить элемент во вложенную таблицу или массив VARRAY, необходимо сначала выделить для него область памяти. Соответствующая операция, не зависящая от присваивания элементу значения, выполняется методом EXTEND. Следует помнить, что указанный метод неприменим к ассоциативным массивам.
Метод EXTEND, как уже было сказано, добавляет элементы в коллекцию. При вызове без аргументов он добавляет один элемент со значением NULL. Вызов EXTEND(n) присоединяет n элементов со значением NULL, а вызов EXTEND(n,i) — n элементов, и всем им присваивает значение i-го элемента. Последняя форма метода применяется к коллекциям, для элементов которых задано ограничение NOT NULL.
Синтаксис перегруженного метода EXTEND:
PROCEDURE EXTEND (n PLS_INTEGER:=1); PROCEDURE EXTEND (n PLS_INTEGER, i PLS_INTEGER);
В следующем примере процедура push добавляет в список один элемент и присваивает ему новое значение:
PROCEDURE push (the_list IN OUT List_t, new_value IN VARCHAR2) AS BEGIN the_list.EXTEND; the_list(the_list.LAST) := new_value; END;
В другом фрагменте кода метод EXTEND используется для включения в коллекцию 10 новых элементов с одинаковыми значениями. Сначала в коллекцию добавляется один элемент, которому явно присваивается нужное значение. При повторном вызове метода EXTEND в коллекцию добавляется еще 9 элементов, которым присваивается значение первого элемента коллекции new_value:
PROCEDURE push_ten (the_list IN OUT List_t, new_value IN VARCHAR2) AS l_copyfrom PLS_INTEGER; BEGIN the_list.EXTEND; l_copyfrom := the_list.LAST; the_list(l_copyfrom) := new_value; the_list.EXTEND (9, l_copyfrom); END;
Граничные условия
Если параметр n имеет значение NULL, метод не выполнит никаких действий.
Возможные исключения
При вызове метода EXTEND для неинициализированной вложенной таблицы или VARRAY инициируется исключение COLLECTION_IS_NULL. Попытка добавить в массив VARRAY элементы, индекс которых превышает максимальный индекс массива в его объявлении, инициирует исключение SUBSCRIPT_BEYOND_LIMIT.
Методы FIRST и LAST
Методы FIRST и LAST возвращают соответственно наименьший и наибольший индексы элементов вложенной таблицы, ассоциативного массива или массива VARRAY. Для ассоциативных массивов, индексируемых строками, эти методы возвращают строки; «наименьшее» и «наибольшее» значения определяются порядком набора символов, используемого данным сеансом. Для других типов коллекций методы возвращают целые числа.
Синтаксис этих функций:
FUNCTION FIRST RETURN PLS_INTEGER | VARCHAR2; FUNCTION LAST RETURN PLS_INTEGER | VARCHAR2;
Так, следующий фрагмент перебирает все элементы коллекции от начала к концу:
FOR indx IN holidays.FIRST .. holidays.LAST LOOP send_everyone_home (indx); END LOOP;
Запомните, что такой цикл будет выполнен корректно (то есть не породит исключения NO_DATA_FOUND) лишь при условии, что коллекция является плотной.
В следующем примере для добавления элементов в конец ассоциативного массива используется оператор COUNT. Цикл FOR с курсором используется для копирования данных из базы в ассоциативный массив. При выборке первой записи коллекция companies пуста, поэтому COUNT возвращает 0.
FOR company_rec IN company_cur LOOP companies ((companies.COUNT) + 1).company_id company_rec.company_id; END LOOP;
Граничные условия
Если методы FIRST и LAST вызываются для инициализированных коллекций, не содержащих ни одного элемента, они возвращают NULL. Для массива VARRAY, всегда содержащего хотя бы один элемент, FIRST всегда возвращает 1, а LAST — то же значение, что и метод COUNT.
Возможные исключения
При вызове методов FIRST и LAST для неинициализированной вложенной таблицы или массива VARRAY инициируется исключение COLLECTION_IS_NULL.
Метод LIMIT
Метод LIMIT возвращает максимальное количество элементов, которое можно определить в массиве VARRAY. В случае вложенной таблицы или ассоциативного массива он возвращает NULL.
Синтаксис метода LIMIT:
FUNCTION LIMIT RETURN PLS_INTEGER;
В следующем примере перед добавлением нового элемента в конец массива VARRAY мы сначала проверяем, есть ли в нем еще свободное место:
IF my_list.LAST < my_list.LIMIT THEN my_list.EXTEND; END IF;
Граничные условия
У метода LIMIT граничных условий не существует.
Ввозможные исключения
Вызов метода LIMIT для неинициализированной вложенной таблицы или массива VARRAY генерирует исключение COLLECTION_IS_NULL.
Методы PRIOR и NEXT
Методы PRIOR и NEXT используются для перемещения по коллекциям — вложенным таблицам, ассоциативным массивам и массивам VARRAY. Метод PRIOR возвращает индекс предыдущего, а метод NEXT — следующего элемента коллекции. Следующая функция возвращает сумму чисел, хранящихся в коллекции list_t:
FUNCTION compute_sum (the_list IN list_t) RETURN NUMBER AS row_index PLS_INTEGER := the_list.FIRST; total NUMBER := 0; BEGIN LOOP EXIT WHEN row_index IS NULL; total := total + the_list(row_index); row_index := the_list.NEXT(row_index); END LOOP; RETURN total; END compute_sum;
Та же программа, но с перебором элементов от последней к первой определенной записи коллекции:
FUNCTION compute_sum (the_list IN list_t) RETURN NUMBER AS row_index PLS_INTEGER := the_list.LAST; total NUMBER := 0; BEGIN LOOP EXIT WHEN row_index IS NULL; total := total + the_list(row_index); row_index := the_list.PRIOR(row_index); END LOOP; RETURN total; END compute_sum;
В данном случае направление перемещения не имеет значения, но в других программах оно может повлиять на результат обработки.
Граничные условия
Методы PRIOR и NEXT для инициализированной коллекции, не содержащей ни одного элемента, возвращают NULL. Если значение i больше или равно COUNT, метод NEXT возвращает NULL; если i меньше или равно FIRST, метод PRIOR возвращает NULL.
Если коллекция не пуста, а параметр i больше или равен COUNT, метод PRIOR возвращает LAST; если параметр i меньше FIRST, метод NEXT возвращает FIRST.
Возможные исключения
Вызов методов PRIOR и NEXT для неинициализированной вложенной таблицы или массива VARRAY генерирует исключение COLLECTION_IS_NULL.
Метод TRIM
Метод TRIM удаляет n последних элементов коллекции — вложенной таблицы или массива VARRAY. Если метод вызывается без аргументов, он удалит только один элемент. Как упоминалось ранее, при совместном использовании методов TRIM и DELETE возможна накладка: если заданный в вызове метода TRIM элемент был уже удален методом DELETE, метод TRIM «повторит» удаление, но считает его частью n, поэтому количество реально удаленных элементов окажется меньшим, чем вы рассчитывали.
Попытка вызова метода TRIM для ассоциативного массива приведет к ошибке компиляции.
Синтаксис метода TRIM:
PROCEDURE TRIM (n PLS_INTEGER:=1);
Следующая функция извлекает из списка последнее значение и возвращает его вызывающему блоку. Операция извлечения реализуется как выборка значения с последующим усечением коллекции на один элемент:
FUNCTION pop (the_list IN OUT list_t) RETURN VARCHAR2 AS l_value VARCHAR2(30); BEGIN IF the_list.COUNT >= 1 THEN /* Сохраняем значение последнего элемента коллекции, || которое будет возвращено функцией */ l_value := the_list(the_list.LAST); the_list.TRIM; END IF; RETURN l_value; END;
Граничные условия
Если значение n равно NULL, метод не выполнит никаких действий.
Возможные исключения
При попытке удалить больше элементов, чем имеется в коллекции, инициируется исключение SUBSCRIPT_BEYOND_COUNT. Если метод TRIM вызывается для неинициализированной вложенной таблицы или массива VARRAY, инициируется исключение COLLECTION_IS_NULL.
Вызывая методы TRIM и DELETE для одной и той же коллекции, можно получить неожиданные результаты. На сколько элементов станет меньше в коллекции, если удалить последний элемент методом DELETE, а затем вызвать метод TRIM с тем же значением параметра? Казалось бы, это приведет к удалению двух элементов, но в действительности оба метода удалят один и тот же элемент. Чтобы избежать накладок, компания Oracle рекомендует использовать только один из этих двух методов при работе с конкретной коллекцией.