
- •Язык pl/sql
- •Блоки программы pl/sql
- •Основы программирования на языке pl/sql
- •Переменные программы
- •Управление ходом выполнения программы
- •Взаимодействие с базами данных
- •Объявление и использование подпрограмм: процедуры и функции
- •Работа с переменными типа запись
- •Атрибуты %type и %rowtype
- •Работа с курсорами
- •Встроенные пакеты утилит
- •Триггеры баз данных
- •Упражнение 4.21. Создание и использование триггеров базы данных
Работа с курсорами
Каждый раз, когда приложение передает SQL-команду СУБД Oracle, сервер открывает для обработки этой команды, по крайней мере, один курсор (рабочую область для команды SQL). Когда программа PL/SQL (или любое другое приложение) представляет команду INSERT, UPDATE или DELETE, Oracle автоматически открывает курсор для ее обработки. Oracle может автоматически обрабатывать команду SELECT, которая возвращает только одну строку.
Для обработки запроса, возвращающего результирующую таблицу из нескольких строк, программа PL/SQL должна явным образом объявить курсор, открыть его, извлекать из него строки по одной, а затем закрыть курсор. Oracle может применять к обработке курсора различные подходы.
Объявление именованного курсора (команды OPEN, FETCH, CLOSE)
Пользователь может явным образом объявить курсор с именем в разделе объявлений блока используя команду CURSOR. После этого можно открыть курсор в теле блока или в разделе обработки исключений с помощью команды OPEN, извлекать из него строки с помощью команды FETCH (обычно в цикле) и в заключение закрыть курсор командой CLOSE. Это наиболее трудоемкий подход, он чреват ошибками и является наихудшим в отношении производительности.
Явно задаваемый цикл FOR для курсора
Пользователь может объявить курсор командой CURSOR, а затем использовать цикл FOR для курсора для автоматического объявления переменной или записи, которая используется для получения строк из курсора. После открытия курсора из него извлекаются строки, и после извлечения последней курсор закрывается. Такой подход проще, менее подвержен ошибкам, обеспечивает более высокую производительность, но все же требует явного объявления курсора.
Неявный цикл FOR для курсора
Кроме объявления переменной или записи для приема строк из курсора пользователь может объявить курсор в самом цикле FOR и обрабатывать соответствующие строки по одной. Такой подход намного проще и обеспечивает такую же производительность, как и использование явного цикла FOR для курсора.
При использовании курсора для обработки большого количества строк циклы FOR курсора обеспечивают значительно большую производительность, чем использование команд OPEN/FETCH/CLOSE. Циклы FOR курсора извлекают за один раз из соответствующего курсора массив в 100 строк, тогда как при использовании для обработки курсора команд OPEN/FETCH/CLOSE Oracle извлекает за один раз лишь одну строку.
Рассмотрим анонимный блок, в котором объявляется и используется очень простой курсор для распечатки избранных полей из всех строк таблицы Users.
При обработке курсора могут использоваться другие, боле сложные подходы, описанные выше. Например, предыдущий блок может быть реализован следующим эквивалентным блоком:
Вэтой версии блока после извлечения из курсора первой записи последующие строки извлекаются из открытого курсора по одной с помощью цикла WHILE. В условии цикла WHILE используется атрибут курсора %FOUND для определения момента, когда из курсора извлекается последняя строка.
Атрибуты явным образом объявленных курсоров, которые можно использовать в языке PL/SQL для базовых курсоров.
cursor%FOUND – атрибут %FOUND получает значение TRUE, если предшествующей команде FETCH соответствует по крайней мере одна строка в базе данных; иначе атрибут получает значение FALSE;
cursor%NOTFOUND – атрибут %NOTFOUND получает значение TRUE, если предшествующей команде FETCH не соответствует ни одной строки в базе данных; иначе атрибут %NOTFOUND получает значение FALSE;
cursor%ISOPEN – атрибут %ISOPEN получает значение TRUE если курсор-источник открыт; иначе атрибут %ISOPEN получает значение FALSE.
cursor%ROWCOUNT – атрибут %ROWCOUNT указывает количество строк, извлеченных до настоящего момента из явным образом объявленного курсора.
Приведем еще одну версию предыдущего блока, в которой для курсора используется явный цикл FOR:
Операции с текущей строкой курсора. Так как программа извлекает строки из результирующей таблицы по одной, она в каждый момент имеет доступ к текущей строке курсора. Когда нужно обновить или удалить текущую строку курсора, метод, используемый для обращения к этой строке, зависит от подхода, применяемого для обработки курсора. При использовании неявного цикла FOR курсор должен выбирать адрес ROWID каждой строки и в условии блока WHERE использовать ROWID строки-источника:
ROWID представляет собой физический адрес, который указывает Oracle, где физически находится соответствующая строка базы данных. Как показано выше, на ROWID можно ссылаться с использованием псевдостолбца ROWID.
Предыдущий блок удаляет две новых записи, вставленные в таблицу Catalogs программами предыдущих примеров. При объявлении курсора для обновления или удаления строк, извлеченных курсором, необходимо объявить определяющий запрос курсора с ключевыми словами FOR UPDATE. Это требование заставляет Oracle блокировать строки в результирующей таблице курсора, что предотвращает изменение или удаление этих строк другими транзакциями, до тех пор, пока для транзакции не будет выполнена фиксация или откат.
Программа PL/SQL, которая явным образом объявляет курсор, может воспользоваться специальным блоком CURRENT OF в условии блока WHERE директив UPDATE или DELETE для обработки текущей строки курсора. Следующий анонимный блок эквивалентен предыдущему блоку:
Эта программа также показывает, что при объявлении курсора пользователь может также объявить один или более параметров курсора (p_catID), которые программа PL/SQL будет использовать для определения критерия выбора записи в курсоре во время выполнения программы.
Работа с коллекциями
В блоках PL/SQL могут объявляться и использоваться коллекции. Коллекция представляет собой переменную – упорядоченный набор аналогичных элементов. Для создания коллекции необходимо сначала объявить либо тип вложенной таблицы, либо тип переменного массива varray (varying array), а затем объявить переменную типа коллекция.
Вложенные таблицы
Программа PL/SQL может использовать тип вложенных таблиц для создания переменных, имеющих один или более столбцов и неограниченное количество строк, подобно таблицам в базе данных.
Следующий анонимный блок демонстрирует объявление типа вложенной таблицы, а затем использование этого типа для объявления новой переменой типа коллекция. Пример также иллюстрирует инициализацию коллекции принадлежащим ей методом конструктора с последующим обращением к отдельным элементам этой вложенной таблицы по индексу (subscript).
Этот пример демонстрирует принципиальные аспекты использования вложенных таблиц:
Перед использованием коллекции (такой как вложенные таблицы) ее необходимо инициализировать, используя конструктор данного типа. PL/SQL автоматически предоставляет пользователю конструктор с тем же именем, которое имеет коллекция. При вызове конструктора для инициализации переменной коллекции можно указать список разделенных запятыми первоначальных элементов коллекции или пустые скобки для инициализации значениями Null.
Коллекция вложенных таблиц может иметь любое количество строк. При необходимости размер таблицы увеличивается или уменьшается динамически в процессе работы программы.
PL/SQL поддерживает несколько методов, которые можно использовать при работе с коллекциями. Для использования метода в программе указывается имя коллекции и после имени название метода с использованием точечной нотации. В таблице приведены методы коллекций, поддерживаемые в языке PL/SQL.
Метод коллекции |
Описание |
COUNT DELETE[(x[,y]...)]
EXISTS(x)
EXTEND[(x[,y])]
FIRST LAST LIMIT
NEXT(x)
PRIOR(x)
TRIM(x) |
Возвращает текущее количество элементов в коллекции Удаляет некоторые или все элементы коллекции, не выделяя освободившееся место другим объектам Возвращает логическое значение TRUE, если существует элемент коллекции с номером х. Иначе метод возвращает значение FALSE Добавляет х копий элемента с номером ув окончание коллекции. Если параметр у опущен, то добавляет к коллекции х элементов со значением null. Если оба параметра х и у опущены, то к коллекции добавляется один элемент со значением null Возвращает индекс первого элемента коллекции Возвращает индекс последнего элемента коллекции Возвращает максимальное количество элементов, которое может содержать коллекция массива varray Возвращает индекс элемента, находящегося в коллекции непосредственно за элементом х Возвращает индекс элемента, коллекции непосредственно предшествующего элементу х Удаляет х элементов коллекции, считая от ее конца |
Приведенный ниже анонимный блок иллюстрирует применение методов коллекции EXTEND, COUNT, FIRST, NEXT и DELETE к вложенной таблице с записями.
Вложенные таблицы являются первоначально плотными (индексы элементов последовательно увеличиваются на единицу), но позднее могут становиться разреженными (в последовательной индексации могут появиться разрывы), если программа удаляет элементы из коллекции. Поэтому циклы, выводящие элементы, не используют переменную счетчика для обращения к элементам коллекции.
Массивы переменной длины
Программа может использовать тип varray (varying array – массив переменной длины) для создания подобных таблицам переменных, которые имеют один или несколько столбцов и ограниченное количество строк.
В основном varray-коллекции подобны коллекциям вложенных таблиц, но имеют следующие важные отличия:
при объявлении переменной типа varray необходимо задать количество элементов в переменном массиве, которое остается постоянным;
массивы переменной длины должны оставаться плотными;программа не может удалять элементы из таких массивов.
Обработка программных исключений
В PL/SQL обработка ошибок осуществляется с помощью исключений и связанных с ними обработчиков. Исключение представляет собой именованное условие возникновения ошибки. Программа генерирует исключение с именем при обнаружении ошибки, а затем передает управление подпрограмме обработки исключений, которая отделена от тела основной программы.
PL/SQL включает в себя много предопределенных исключений, соответствующих типичным ошибкам в Oracle. Когда программа сталкивается с предопределенным исключением, она автоматически передает управление соответствующему обработчику исключений – программа не должна явно выполнять проверку предопределенных исключений.
PL/SQL идентифицирует несколько предопределенных исключений:
программа автоматически генерирует исключение NO_ DATA_FOUND, если результирующая таблица команды SELECT INTO не содержит ни одной строки;
программа автоматически генерирует исключение ТОО_ MANY_ROWS, если результирующая таблица команды SELECT INTO содержит более одной строки.
Полный список предопределенных исключений представлен в Руководстве и Справочнике пользователя по Oracle PL/SQL.
Следующий анонимный блок включает в себя обработчики для предопределенных исключений NO_DATA_FOUND и TOO_MANY_ROWS:
Пример демонстрирует несколько важных моментов обработки исключений:
Раздел обработки исключений можно включать в любой блок – как анонимный блок, так и его подпрограмма (процедура printOrder) имеют свои собственные разделы обработки исключений.
Первый вызов процедуры printOrder не генерирует никаких исключений и печатает код единственного заказа, размещенного клиентом с кодом 1.
Второй вызов процедуры printOrder генерирует предопределенное исключение NO_DATA_FOUND, поскольку оператор SELECT ... INTO в процедуре не извлекает строк. Локальный по отношению к процедуре обработчик исключения обрабатывает исключение путем распечатки сообщения, а затем возвращает управление вызывающей программе (анонимному блоку). Этот блок затем вызывает процедуру printOrder в третий раз.
Третий вызов процедуры printOrder генерирует предопределенное исключение TOO_MANY_ROWS, поскольку оператор SELECT ... INTO в процедуре возвращает более одной строки. Раздел обработки исключений процедуры не обрабатывает исключение TOO_MANY_ROWS локально, поэтому исключение передается вызывающей программе (анонимному блоку), у которой есть обработчик исключения ТОО_ MANY_ROWS. Обработчик печатает сообщение, а затем передает управление вызывающей программе (страница SQL-команд Oracle SQL Developer). Выполнение анонимного блока останавливается, и четвертое обращение к процедуре printOrder никогда не произойдет.
Если необрабатываемое исключение генерируется или передается внешнему блоку, то этот блок останавливает выполнение и возвращает в вызывающую программу номер ошибки и сообщение, которое соответствует исключению.
В разделе объявлений блока можно также объявлять пользовательские исключения. Однако чтобы сгенерировать исключение, программа должна явно выполнять проверку пользовательского исключения.
Следующий анонимный блок демонстрирует использование пользовательских исключений.
Данный пример иллюстрирует несколько интересных аспектов обработки пользовательских исключений:
пользовательское исключение объявляется в секции объявлений PL/SQL-блока с помощью ключевого слова EXCEPTION;
пользовательское исключение генерируется с помощью PL/SQL-команды RAISE;
программа может использовать обработчик WHEN OTHERS для обработки всех исключений, не имеющих специальных обработчиков;
в программе можно использовать специальные функции SQLCODE и SQLERRM, которые возвращают соответственно номер и текст сообщения последней ошибки Oracle.
Типы программ PL/SQL
Рассмотрим различные типы программ, которые можно создавать с помощью языка PL/SQL (анонимные блоки, процедуры, функции, пакеты и триггеры).
Анонимные блоки языка PL/SQL
Все предыдущие примеры представляют собой анонимные блоки PL/SQL, Анонимным блоком называется блок, находящийся внутри создаваемого приложения. Такой блок не имеет имени и не хранится в базе данных. Приложение отправляет этот блок кода серверу баз данных для обработки в режиме исполнения.
Хранимые процедуры и функции
Выше было показано, как объявлять и использовать подпрограммы PL/SQL (процедуры и функции) внутри блоков. Процедуры и функции можно хранить как скомпилированные блоки логики приложения внутри базы данных Oracle в виде именованных объектов схемы.
Преимущества хранения подпрограмм в базе данных:
Возможность повторного использования кода и повышение продуктивности разработчиков. Концентрация общих процедур и функций в базе данных позволяет использовать эти подпрограммы в любом приложении. Рациональное использование хранимых процедур и функций может повысить продуктивность разработчика и упростить создание приложения.
Целостность данных. Как только правильность хранимых подпрограмм подтверждена, приложения могут полагаться на них как на методы доступа к данным, не нарушающие целостность данных в базе.
Безопасность. Хранимые подпрограммы представляют собой скомпилированные программы для доступа к информации, которые скрывают доступные для них основные структуры базы данных. Только владелец подпрограммы, а не все вызывающие ее пользователи, имеет привилегии для доступа к основным структурам данных.
Для создания хранимой процедуры в базе данных Oracle используется SQL-команда CREATE PROCEDURE. Хранимые процедуры объявляются так же, как и процедуры в разделе объявлений блоков PL/SQL. При объявлении хранимой процедуры можно использовать следующие параметры для указания области привилегий, которая используется в Oracle при выполнении данной процедуры:
AUTHID CURRENT_USER. Если процедура создается с данной опцией, то Oracle выполняет ее, используя область привилегий вызывающего процедуру пользователя, включая роли. Для успешного выполнения процедуры вызывающий ее пользователь должен иметь привилегии, необходимые для доступа ко всем объектам базы данных, к которым имеются SQL-обращения в теле процедуры.
AUTHID DEFINER. Опция по умолчанию. Oracle выполняет процедуру, используя область привилегий владельца процедуры, исключая роли. Для успешного выполнения процедуры ее владелец, должен иметь привилегии, необходимые для доступа ко всем объектам базы данных, к которым имеются SQL-обращения в теле хранимой процедуры.
Чтобы упростить управление привилегиями для пользователей приложений, при создании хранимой процедуры следует применять используемую по умолчанию опцию AUTHID DEFINER. Этот способ позволяет давать привилегии не всем пользователям, которым приходится вызывать процедуру.
Приведенный ниже пример иллюстрирует создание и хранение уже известной нам процедуры printLine в базе данных Oracle:
Раскрыв в навигаторе узел Procedures, увидим процедуру printLine.
Теперь процедуру printLine хранится в базе и можно использовать ее в любой программе PL/SQL. Выполним блок PL/SQL, использующий процедуру printLine:
Для создания хранимой функции используется команда CREATE FUNCTION. Функция описывается, как в блоке PL/SQL. Для нее нужно объявить тип возвращаемого значения, а в теле функции должны присутствовать один или несколько операторов RETURN для возвращения функцией ее значения. Рассмотрим пример создания и хранение функция orderTotal в базе данных Oracle:
Раскрыв в навигаторе узел Functions, увидим функцию orderSum.
Теперь введите следующий блок для расчета суммы заказов 1 и 2:
Пакеты
Пакет – группа процедур, функций и других PL/SQL-конструкций, которые хранятся в базе данных как единое целое. Пакеты полезны для создания группы из нескольких процедур и функций, относящихся к определенному приложению.
Пакет состоит из двух частей:
Спецификация пакета. Определяет интерфейс пакета. В ней объявляются переменные пакета, константы, курсоры, процедуры, функции и другие конструкции, которые необходимо сделать доступными для программ за пределами пакета (все, что объявлено в спецификации пакета, является общедоступным). Спецификация пакета объявляется с помощью SQL-команды CREATE PACKAGE.
Тело пакета. В теле определяются все общедоступные процедуры и функции, объявленные в спецификации. Тело пакета может включать определения других конструкций, не объявленных в спецификации. Такие конструкции являются частными (доступными только для программ внутри пакета). Тело пакета объявляется с помощью SQL-команды CREATE PACKAGE BODY.
Отделение спецификации пакета (интерфейса) от его тела позволяет модифицировать реализующую пакет логику без нарушения зависимостей, которые опираются на API пакета.
Все переменные, константы и курсоры, объявленные в спецификации либо в теле пакета, вне подпрограммы считаются глобальными. В отличие от локальных переменных, констант и курсоров, объявленных внутри определенных процедур и функций, глобальные конструкции доступны всем процедурам и функциям пакета. Их состояние в течение сеанса остается постоянным независимо от подпрограмм пакета. Соглашение по объявлению глобальных переменных (и других глобальных конструкций) пакета предполагает использование префикса g_.
Рассмотрим создание простого пакета catMgmt, демонстрирующего функции, доступные пакетам PL/SQL. Сначала нужно создать спецификацию пакета:
Далее создайте тело пакета, используя приведенную ниже директиву.
Теперь введите анонимный блок PL/SQL, использующий процедуру insertCat пакета catMgmt для вставки новой строки в таблицу Catalogs:
Обратите внимание на то, что при ссылке на объект типа пакет (на глобальную переменную или подпрограмму) необходимо использовать точечную нотацию для идентификации объекта пакета по имени самого пакета.
Введите анонимный блок для изменения названия нового каталога на «Разное» с использованием процедуры updateCat пакета catMgmt.
Теперь введите анонимный блок PL/SQL для выполнения процедуры deleteCat пакета catMgmt, которая удаляет новую часть таблицыCatalogs.
В заключение введите анонимный блок для вывода количества строк таблицы Catalogs, обработанных в текущем сеансе с помощью пакета catMgmt.
Процедура printCatProcessed показывает, что глобальная переменная g_rowsProcessed сохраняет свое состояние независимо от обращений к отдельным подпрограммам пакета.
Команда EXECUTE представляет собой другой способ выполнения PL/SQL-подпрограмм. Вызывая функцию, необходимо создать переменную для присвоения ей возвращаемого функцией значения.