Открыть сервис

Обработка исключений PL/SQL

Обработка исключений PL/SQL — это механизм языка программирования PL/SQL, предназначенный для обнаружения, перехвата и обработки ошибок, возникающих во время выполнения программных блоков (анонимных блоков, процедур, функций, пакетов и триггеров). Данный механизм позволяет разработчику контролировать поток выполнения программы при возникновении исключительных ситуаций, предотвращая аварийное завершение работы и обеспечивая корректную реакцию на ошибки, такие как нарушение целостности данных, деление на ноль, отсутствие данных или системные сбои. Обработка исключений является неотъемлемой частью надёжного и устойчивого кода PL/SQL, применяемого в системах управления базами данных Oracle.

Основные понятия и принципы

В PL/SQL исключение (exception) представляет собой событие, которое нарушает нормальное выполнение программы. При возникновении ошибки система генерирует исключение, после чего управление передаётся в специальный раздел обработки исключений. Если исключение не обработано, оно распространяется по стеку вызовов до тех пор, пока не будет перехвачено или не приведёт к завершению сеанса с ошибкой.

Типы исключений

Исключения в PL/SQL делятся на три категории:

  1. Встроенные (предопределённые) исключения. Связаны с наиболее распространёнными ошибками времени выполнения. Примеры: NO_DATA_FOUND (выборка не вернула строк), TOO_MANY_ROWS (выборка вернула более одной строки), ZERO_DIVIDE (деление на ноль), DUP_VAL_ON_INDEX (попытка вставить дубликат значения в столбец с уникальным индексом), INVALID_CURSOR (недопустимая операция с курсором), VALUE_ERROR (ошибка преобразования или усечения данных).
  2. Пользовательские исключения. Определяются разработчиком для обработки специфических бизнес-правил или логических ошибок. Объявляются в разделе DECLARE как переменные типа EXCEPTION.
  3. Исключения, связанные с кодами ошибок Oracle. Любая ошибка Oracle, не имеющая предопределённого имени, может быть перехвачена с помощью прагмы EXCEPTION_INIT или обработчика OTHERS с последующим анализом кода ошибки через функцию SQLCODE.

Синтаксис и структура

Обработка исключений реализуется в разделе EXCEPTION блока PL/SQL, который следует за исполняемым разделом BEGIN...END. Общий синтаксис:

``sql DECLARE -- объявления переменных, констант, курсоров пользовательское_исключение EXCEPTION; BEGIN -- исполняемый код RAISE пользовательское_исключение; -- генерация исключения EXCEPTION WHEN исключение1 [OR исключение2] THEN -- действия по обработке WHEN пользовательское_исключение THEN -- действия по обработке WHEN OTHERS THEN -- обработчик всех прочих исключений END; ``

Ключевые элементы

  • Раздел EXCEPTION. Может содержать несколько обработчиков WHEN. Проверка условий выполняется последовательно, и выполняется первый подходящий блок.
  • Обработчик WHEN OTHERS. Является универсальным и перехватывает любые исключения, не обработанные предыдущими условиями. Должен размещаться последним в разделе.
  • Оператор RAISE. Используется для явной генерации исключения (как предопределённого, так и пользовательского) или для повторной генерации уже перехваченного исключения (например, в блоке WHEN OTHERS).
  • Прагма EXCEPTION_INIT. Связывает пользовательское имя исключения с конкретным кодом ошибки Oracle. Записывается в разделе объявлений: PRAGMA EXCEPTION_INIT(имя_исключения, -номер_ошибки);.

Функции для получения информации об ошибке

Внутри раздела обработки исключений доступны две встроенные функции:

  • SQLCODE — возвращает числовой код ошибки Oracle. Для пользовательских исключений, не связанных с кодом Oracle, возвращает значение +1.
  • SQLERRM — возвращает текстовое сообщение, соответствующее коду ошибки. Для пользовательских исключений возвращает строку «User-Defined Exception».

Эти функции позволяют разработчику динамически формировать сообщения об ошибках или записывать их в журнал.

Примеры использования

Пример 1: Обработка встроенного исключения

``sql DECLARE v_employee_name VARCHAR2(100); BEGIN SELECT first_name INTO v_employee_name FROM employees WHERE employee_id = 9999; -- такого ID нет EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('Сотрудник с указанным ID не найден.'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Произошла ошибка: ' || SQLERRM); END; ``

Пример 2: Пользовательское исключение и прагма EXCEPTION_INIT

``sql DECLARE e_salary_too_high EXCEPTION; PRAGMA EXCEPTION_INIT(e_salary_too_high, -20001); v_salary NUMBER := 50000; v_max_salary CONSTANT NUMBER := 30000; BEGIN IF v_salary > v_max_salary THEN RAISE_APPLICATION_ERROR(-20001, 'Зарплата превышает допустимый максимум.'); END IF; EXCEPTION WHEN e_salary_too_high THEN DBMS_OUTPUT.PUT_LINE('Ошибка: ' || SQLERRM); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Неизвестная ошибка'); END; ``

Пример 3: Распространение исключения

Если исключение не обработано во внутреннем блоке, оно передаётся во внешний блок. Вложенные блоки позволяют реализовать локальную обработку и, при необходимости, повторную генерацию исключения для обработки на более высоком уровне.

``sql BEGIN BEGIN -- внутренний блок RAISE ZERO_DIVIDE; EXCEPTION WHEN ZERO_DIVIDE THEN DBMS_OUTPUT.PUT_LINE('Деление на ноль во внутреннем блоке. Обрабатывается локально.'); RAISE; -- повторная генерация END; EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Исключение перехвачено во внешнем блоке: ' || SQLERRM); END; ``

Рекомендации по использованию

  • Использовать OTHERS только для логирования и повторной генерации. Не следует подавлять неизвестные ошибки без анализа, так как это может скрыть серьёзные проблемы.
  • Избегать излишней детализации. Обработка каждого возможного встроенного исключения отдельно оправдана только при необходимости разной реакции на разные типы ошибок.
  • Применять RAISE_APPLICATION_ERROR. Для генерации бизнес-ошибок с пользовательским кодом (в диапазоне от -20000 до -20999) и сообщением рекомендуется использовать процедуру RAISE_APPLICATION_ERROR.
  • Логировать ошибки. В продуктивной среде следует записывать информацию об исключениях (код, сообщение, стек вызовов) в таблицу журнала ошибок для последующего анализа.
  • Не использовать исключения для управления потоком. Обработка исключений предназначена для аномальных ситуаций, а не для замены условных операторов (например, IF).

Сравнение с другими языками

Механизм исключений PL/SQL концептуально схож с обработкой исключений в Java, C++ или Python, но имеет ряд особенностей. В отличие от Java, в PL/SQL нет проверяемых исключений (checked exceptions) и иерархии классов исключений. Все исключения являются объектами типа EXCEPTION, а не классами. Обработка через OTHERS аналогична блоку catch (Exception e) в Java, но не позволяет различать типы исключений на основе наследования. Встроенные исключения не имеют параметров, что отличает их от исключений в Python, где можно передавать произвольные данные.

Источники

  • Oracle Database PL/SQL Language Reference, 19c (раздел «PL/SQL Error Handling»)
  • Feuerstein S., Pribyl B. «Oracle PL/SQL Programming», 6th Edition, O'Reilly Media, 2014
  • Документация Oracle: «PL/SQL Exceptions»

BFOmetr — база данных и аналитика по компаниям России.

На главную BFOmetr →