В PL/SQL предусмотрены механизмы перехвата и обработки ошибок, возникающих при выполнении программы. При обнаружении ошибки, генерируется исключительная ситуация, обработка которой производится в разделе EXCEPTION. Существуют два класса исключительных ситуаций: стандартные ситуации и ситуации, определяемые пользователем. Стандартные ситуации в свою очередь также делятся на два типа: имеющие и не имеющие предопределенное имя. Имеющие предопределенное имя исключительные ситуации помимо кода имеют еще и стандартное имя, которое используется для идентификации исключения. В следующей таблице приведены примеры некоторых стандартных исключительных ситуаций, имеющих предопределенные имена.
Исключительная ситуация
Код
Описание
ZERO_DIVIDE
Попытка деления на ноль
NO_DATA_FOUND
Предложение SELECT...INTO не возвращает ни одной строки
TOO_MANY_ROWS
Предложение SELECT...INTO возвращает более одной строки
INVALID_CURSOR
Попытка выполнения запрещенной операции с курсором (например, закрытие неоткрытого курсора)
CURSOR_ALREADY_OPEN
Попытка открытия уже открытого курсора
VALUE_ERROR
Арифметическая ошибка, ошибка преобразования, усечения или ограничения
INVALID_NUMBER
Отказ в преобразовании строки символов в число
Пользовательские исключительные ситуации описываются в разделе DECLARE, устанавливаются в выполняемом разделе, а обрабатываются в разделе EXCEPTION. Описание пользовательской исключительной ситуации выполняется заданием имени исключения и фразы EXCEPTION. Для генерации исключительной ситуации и передачи управления обработчику пользовательской исключительной ситуации в случае обнаружения ошибки используется оператор
RAISE имя_пользовательского_исключения
Для перехвата исключительной ситуации любого типа в раздел EXCEPTION должна быть включена фраза
WHEN имя_исключения THEN текст_обработчика_исключения;
тогда при возникновении соответствующей ошибки, вместо прекращения исполнения программы и выдачи типового сообщения об ошибке, будет выполняться созданный пользователем вариант обработки исключения. Если необходимо, чтобы две или более исключительные ситуации обрабатывались одинаково, то они должны быть записаны в одном операторе WHEN, разделенные ключевым словом OR. Для перехвата всех неописанных исключительных ситуаций используется специальный обработчик OTHERS, который записывается последним в блоке EXCEPTION.
Генерация исключительной ситуации с выдачей соответствующего сообщения в рабочую среду в случае обнаружения ошибки может быть выполнена с помощью следующего оператора:
RAISE_APPLICATION_ERROR (errnum, errtext);
где errnum – код ошибки в диапазоне -20000 .. –20999;
errtext - символьная строка длиной до 512 байт.
При возникновении исключительной ситуации и отсутствии соответствующего обработчика в данном блоке система пытается найти такой обработчик в блоках, охватывающих этот блок. При отсутствии обработчика система вернет ошибку “необработанное исключение”.
Примеры.
Создать программу, которая осуществляет в таблице KNIGA повышение цен на книги жанра «Фантастика». При этом, при стоимости книги менее 2000, цена увеличивается на 20 %, а при стоимости больше или равной 2000 она увеличивается на 10 %.
Данная задача реализуется с помощью явно объявленного пользователем курсора. При этом в первых двух вариантах показываются возможности использования обычного курсора и курсора с параметром. Приведенное решение демонстрирует два варианта обработки явно объявленного курсора: явную и неявную формы. В явной форме обработки по завершении просмотра строк активного набора осуществляется выход из цикла обработки. При этом используется курсорный атрибут %NOTFOUND. В неявной форме обработки курсора используется конструкция цикл FOR с курсором. Модифицированное значение цены записывается обратно в таблицу KNIGA с использованием конструкции WHERE CURRENT OF, при этом системе с помощью конструкции FOR UPDATE OF ЦЕНА указывается, что будет осуществляться обновление значений столбца ЦЕНА таблицы KNIGA. В программе неявным способом объявлены переменная типа запись ZAP и скалярная переменная NEW_CENA.
а) Использование обычного курсора:
DECLARE
CURSOR KUR IS --явное объявление курсора KUR
SELECT КОД_КНИГИ, ЦЕНА FROM KNIGA
WHERE ЖАНР='Фантастика' FOR UPDATE OF ЦЕНА;
ZAP KUR%ROWTYPE; --объявление переменной-записи
NEW_CENA KNIGA.ЦЕНА%TYPE; --объявление переменной
BEGIN
OPEN KUR; --явное открытие курсора
LOOP
FETCH KUR INTO ZAP; --выборка текущей записи
EXIT WHEN KUR%NOTFOUND; --выход из цикла
IF ZAP.ЦЕНА < 2000 THEN --изменение цены
NEW_CENA := ZAP.ЦЕНА*1.2;
ELSE
NEW_CENA := ZAP.ЦЕНА*1.1;
END IF;
UPDATE KNIGA
SET ЦЕНА = NEW_CENA --обновление цены
WHERE CURRENT OF KUR;
END LOOP;
CLOSE KUR; --явное закрытие курсора
COMMIT; --завершение транзакции
END;
б) Использование курсора с параметром:
DECLARE
CURSOR KUR (GANR KNIGA.ЖАНР%TYPE) IS --курсор имеет параметр
SELECT КОД_КНИГИ, ЦЕНА FROM KNIGA
WHERE ЖАНР=GANR FOR UPDATE OF ЦЕНА;
ZAP KUR%ROWTYPE;
NEW_CENA KNIGA.ЦЕНА%TYPE;
BEGIN
OPEN KUR ('Фантастика'); -- значение параметра
LOOP
FETCH KUR INTO ZAP;
EXIT WHEN KUR%NOTFOUND;
IF ZAP.ЦЕНА < 2000 THEN
NEW_CENA := ZAP.ЦЕНА*1.2;
ELSE
NEW_CENA := ZAP.ЦЕНА*1.1;
END IF;
UPDATE KNIGA SET ЦЕНА = NEW_CENA
WHERE CURRENT OF KUR;
END LOOP;
CLOSE KUR;
COMMIT;
END;
в) Использование цикла FOR с курсором:
DECLARE
NEW_CENA KNIGA.ЦЕНА%TYPE;
CURSOR KUR IS
SELECT КОД_КНИГИ, ЦЕНА FROM KNIGA
WHERE ЖАНР='Фантастика' FOR UPDATE OF ЦЕНА;
BEGIN
FOR ZAP IN KUR LOOP --неявная обработка курсора
IF ZAP.ЦЕНА < 2000 THEN --переменная ZAP неявно объявляется системой