Погружение в базы данных Oracle

Данная книга является руководством для начинающих специалистов в области анализа и обработки данных. В книге рассматривается язык SQL и его процедурное расширение PL/SQL от компании Oracle.

Купить книгу

SQL без слёз

Цель книги заключается в том, чтобы научить любого человека работать с реляционными базами данных и получать из них необходимую информацию посредством выполнения SQL-запросов.

Скачать книгу

 ›  ›  ›  › Встроенные методы коллекций в PL/SQL Oracle

Встроенные методы коллекций в 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 рекомендует использовать только один из этих двух методов при работе с конкретной коллекцией.

Метки: , .

Записи по теме

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *