Структура блока в PL/SQL Oracle
Что такое блок в PL/SQL?
В PL/SQL, как и в большинстве других процедурных языков, наименьшей единицей группировки кода является блок. Он представляет собой фрагмент программного кода, определяющий границы выполнения и области видимости для объявлений переменных и обработки исключений. PL/SQL позволяет создавать как именованные, так и анонимные блоки (то есть блоки, не имеющие имени), которые представляют собой пакеты, процедуры, функции, триггеры или объектные типы.
Структура блока PL/SQL
Блок PL/SQL может содержать до четырех разделов, однако только один из них является обязательным.
- Заголовок. Используется только в именованных блоках, определяет способ вызова именованного блока или программы. Не обязателен.
- Раздел объявлений. Содержит описания переменных, курсоров и вложенных блоков, на которые имеются ссылки в исполняемом разделе и разделе исключений. Не обязателен.
- Исполняемый раздел. Команды, выполняемые ядром PL/SQL во время работы приложения. Обязателен.
- Раздел исключений. Обрабатывает исключения (предупреждения и ошибки). Не обязателен.
Структура блока PL/SQL для процедуры показана на рисунке ниже.
На рисунке показана процедура, содержащая все четыре раздела. Этот конкретный блок начинается с ключевого слова PROCEDURE и, как и все блоки, завершается ключевым словом END.
Анонимные блоки
Когда кто-то хочет остаться неизвестным, он не называет своего имени. То же можно сказать и об анонимном блоке PL/SQL, показанном на рисунке ниже: в нем вообще нет раздела заголовка, блок начинается ключевым словом DECLARE (или BEGIN). Анонимный блок не может быть вызван из другого блока, поскольку он не имеет идентификатора, по которому к нему можно было бы обратиться. Таким образом, анонимный блок представляет собой контейнер для хранения команд PL/SQL — обычно с вызовами процедур и функций. Поскольку анонимные блоки могут содержать собственные разделы объявлений и исключений, разработчики часто используют вложение анонимных блоков для ограничения области видимости идентификаторов и организации обработки исключений в более крупных программах.
Общий синтаксис анонимного блока PL/SQL
[ DECLARE ... объявления ... ] BEGIN ... одна или несколько исполняемых команд ... [ EXCEPTION ... команды обработки исключений ... ] END;
Квадратными скобками обозначаются необязательные составляющие синтаксиса. Анонимный блок обязательно содержит ключевые слова BEGIN и END, и между ними должна быть как минимум одна исполняемая команда. Несколько примеров:
Простейший анонимный блок:
BEGIN DBMS_OUTPUT.PUT_LINE(SYSDATE); END;
Анонимный блок с добавлением раздела объявлений:
DECLARE l_right_now VARCHAR2(9); BEGIN l_right_now := SYSDATE; DBMS_OUTPUT.PUT_LINE (l_right_now); END;
Анонимный блок выполняет серию команд, а затем завершает свою работу, то есть по сути является аналогом процедуры. Фактически каждый анонимный блок является анонимной процедурой. Они используются в различных ситуациях, в которых код PL/SQL выполняется либо непосредственно, либо как часть другой программы.
Именованные блоки
Хотя анонимные блоки PL/SQL применяются во многих приложениях Oracle, вероятно, большая часть написанного вами кода будет оформлена в виде именованных блоков. Ранее вы уже видели несколько примеров хранимых процедур (см. выше рисунок с общей структурой блока PL/SQL) и знаете, что их главной особенностью является наличие заголовка. Заголовок процедуры выглядит так:
PROCEDURE [схема.]имя [ ( параметр [, параметр ... ] ) ] [AUTHID {DEFINER | CURRENT_USER}]
Заголовок функции в целом очень похож на него, но дополнительно содержит ключевое слово RETURN:
FUNCTION [схема.]имя [ ( параметр [, параметр ... ] ) ] RETURN возвращаемый_тип [AUTHID {DEFINER | CURRENT_USER}] [DETERMINISTIC] [PARALLEL ENABLE ...] [PIPELINED [USING...] | AGGREGATE USING...]
Поскольку Oracle позволяет вызывать некоторые функции из SQL-команд, заголовок функции содержит больше необязательных компонентов, чем заголовок процедуры (в зависимости от функциональности и производительности исполнительной среды SQL).
Вложенные блоки
PL/SQL, как и языки Ada и Pascal, относится к категории языков с блочной структурой, то есть блоки PL/SQL могут вкладываться в другие блоки. С другой стороны, язык C тоже поддерживает блоки, но стандартный C не является строго блочно-структурированным языком, потому что вложение подпрограмм в нем не допускается.
В следующем примере PL/SQL показана процедура, содержащая анонимный вложенный блок:
PROCEDURE calc_totals IS year_total NUMBER; BEGIN year_total := 0; /* Начало вложенного блока */ DECLARE month_total NUMBER; BEGIN month_total := year_total / 12; END set_month_total; /* Конец вложенного блока */ END;
Ограничители /* и */ обозначают начало и конец комментариев. Анонимные блоки также могут вкладываться более чем на один уровень (см. рисунок ниже).
Главное преимущество вложенных блоков заключается в том, что они позволяют ограничивать области видимости и действия синтаксических элементов кода.
Область действия
В любом языке программирования областью действия (scope) называется механизм определения «сущности», обозначаемой некоторым идентификатором. Если программа содержит более одного экземпляра идентификатора, то используемый экземпляр определяется языковыми правилами области действия. Управление областью видимости идентификаторов не только помогает контролировать поведение программы, но и уменьшает вероятность того, что программист по ошибке изменит значение не той переменной.
В PL/SQL переменные, исключения, модули и некоторые другие структуры являются локальными для блока, в котором они объявлены. Когда выполнение блока будет завершено, все эти структуры становятся недоступными.
У каждой переменной PL/SQL имеется некоторая область действия — участок программы (блок, подпрограмма или пакет), в котором можно ссылаться на эту переменную. Рассмотрим следующее определение пакета:
PACKAGE scope_demo IS g_global NUMBER; PROCEDURE set_global (number_in IN NUMBER); END scope_demo; PACKAGE BODY scope_demo IS PROCEDURE set_global (number_in IN NUMBER) IS l_salary NUMBER := 10000; l_count PLS_INTEGER; BEGIN <<local_block>> DECLARE l_inner NUMBER; BEGIN SELECT COUNT (*) INTO l_count FROM employees WHERE department_id = l_inner AND salary > l_salary; END local_block; g_global := number_in; END set_global; END scope_demo;
Переменная scope_demo.g_global может использоваться в любом блоке любой схемы, обладающем привилегией EXECUTE для scope_demo.
Переменная l_salary может использоваться только в процедуре set_global.
Переменная l_inner может использоваться только в локальном или вложенном блоке. Обратите внимание на использование метки local_block для присваивания имени вложенному блоку.
Видимость
Важным свойством переменной, связанным с областью ее действия, является видимость. Данное свойство определяет, можно ли обращаться к переменной только по ее имени, или же к имени необходимо добавлять префикс.
«Видимые» идентификаторы
Начнем с тривиального случая:
DECLARE first_day DATE; last_day DATE; BEGIN first_day := SYSDATE; last_day := ADD_MONTHS (first_day, 6); END;
Обе переменные first_day и last_day объявляются в том же блоке, где они используются, поэтому при обращении к ним указаны только имена без уточняющих префиксов. Такие идентификаторы называются видимыми. В общем случае видимым идентификатором может быть:
идентификатор, объявленный в текущем блоке;
- идентификатор, объявленный в блоке, который включает текущий блок;
- отдельный объект базы данных (таблица, представление и т. д.) или объект PL/SQL (процедура, функция), владельцем которого вы являетесь;
- отдельный объект базы данных или объект PL/SQL, на который у вас имеются соответствующие привилегии и который определяется видимым синонимом;
- индексная переменная цикла (видима и доступна только внутри цикла).
PL/SQL также позволяет обращаться к существующим объектам, которые не находятся в пределах непосредственной видимости блока.
Уточненные идентификаторы
Типичным примером идентификаторов, невидимых в области кода, где они используются, являются идентификаторы, объявленные в спецификации пакета (имена переменных, типы данных, имена процедур и функций). Чтобы обратиться к такому объекту, необходимо указать перед его именем префикс и точку (аналогичным образом имя столбца уточняется именем таблицы, в которой он содержится). Например:
price_util.compute_means — программа с именем compute_means из пакета price_util.
math.pi — константа с именем pi, объявленная и инициализированная в пакете math.
Дополнительное уточнение может определять владельца объекта. Например, выражение:
scott.price_util.compute_means
обозначает процедуру compute_means пакета price_util, принадлежащего пользователю Oracle с учетной записью scott.
Уточнение идентификаторов именами модулей
PL/SQL предоставляет несколько способов уточнения идентификаторов для логического разрешения ссылок. Так, использование пакетов позволяет создавать переменные с глобальной областью действия. Допустим, имеется пакет company_pkg и в спецификации пакета объявлена переменная с именем last_company_id:
PACKAGE company_pkg IS last_company_id NUMBER; ... END company_pkg;
На переменную можно ссылаться за пределами пакета — необходимо лишь указать перед ее именем имя пакета:
IF new_company_id = company_pkg.last_company_id THEN
По умолчанию значение, присвоенное переменной пакетного уровня, продолжает действовать на протяжении текущего сеанса базы данных; оно не выходит из области действия вплоть до разрыва подключения.
Идентификатор также можно уточнить именем модуля, в котором он определен:
PROCEDURE calc_totals IS salary NUMBER; BEGIN ... DECLARE salary NUMBER; BEGIN salary := calc_totals.salary; END; ... END;
В первом объявлении создается переменная salary, областью действия которой является вся процедура. Однако затем во вложенном блоке объявляется другой идентификатор с тем же именем. Поэтому ссылка на переменную salary во внутреннем блоке всегда сначала разрешается по объявлению в этом блоке, где переменная видима безо всяких уточнений. Чтобы во внутреннем блоке обратиться к переменной salary, объявленной на уровне процедуры, необходимо уточнить ее имя именем процедуры (cal_totals.salary).
Этот метод уточнения идентификаторов работает и в других контекстах. Что произойдет при выполнении следующей процедуры (order_id — первичный ключ таблицы orders):
PROCEDURE remove_order (order_id IN NUMBER) IS BEGIN DELETE orders WHERE order_id = order_id; -- Катастрофа! END;
Этот фрагмент удалит из таблицы orders все записи независимо от переданного значения order_id. Дело в том, что механизм разрешения имен SQL сначала проверяет имена столбцов и только потом переходит к идентификаторам PL/SQL. Условие WHERE (order_id = order_id) всегда истинно, поэтому все данные пропадают.
Возможное решение проблемы выглядит так:
PROCEDURE remove_order (order_id IN NUMBER) IS BEGIN DELETE orders WHERE order_id = remove_order.order_id; END;
В этом случае при разборе имя переменной будет интерпретировано правильно. (Решение работает даже при наличии в пакете функции с именем remove_order.order_id.)
В PL/SQL установлен целый ряд правил разрешения конфликтов имен, а этой проблеме уделяется серьезное внимание. И хотя знать эти правила полезно, лучше использовать уникальные идентификаторы, чтобы избежать подобных конфликтов. Старайтесь писать надежный код! Если же вы не хотите уточнять каждую переменную, чтобы обеспечить ее уникальность, вам придется тщательно проработать схему назначения имен для предотвращения подобных конфликтов.
Вложенные программы
Завершая тему вложения, области действия и видимости, стоит упомянуть о такой полезной возможности PL/SQL, как вложенные программы (nested programs). Вложенная программа представляет собой процедуру или функцию, которая полностью размещается в разделе объявлений внешнего блока. Вложенная программа может обращаться ко всем переменным и параметрам, объявленным ранее во внешнем блоке, как показывает следующий пример:
PROCEDURE calc_totals (fudge_factor_in IN NUMBER) IS subtotal NUMBER := 0; /* Начало вложенного блока (в данном случае процедуры). | Обратите внимание: процедура полностью размещается | в разделе объявлений calc_totals. */ PROCEDURE compute_running_total (increment_in IN PLS_INTEGER) IS BEGIN /* Переменная subtotal (см. выше) видима и находится в области действия */ subtotal := subtotal + increment_in * fudge_factor_in; END; /* Конец вложенного блока */ BEGIN FOR month_idx IN 1..12 LOOP compute_running_total (month_idx); END LOOP; DBMS_OUTPUT.PUT_LINE('Годовой итог: ' || subtotal); END;
Вложенные программы упрощают чтение и сопровождение кода, а также позволяют повторно использовать логику, задействованную в нескольких местах блока.
Метки: Oracle, Анонимные блоки, Видимость, Вложенные блоки, Именованные блоки, Область действия.