Firebird РУКОВОДСТВО РАЗРАБОТЧИКА БАЗ ДАННЫХ
Шрифт:
В выполняемых процедурах SUSPEND имеет точно такой эффект, что и END.
Рекурсивные процедуры
Если процедура вызывает саму себя, она является рекурсивной. Рекурсивные процедуры полезны для задач, включающих повторяющиеся шаги.
Каждое обращение к процедуре называется экземпляром (instance), поскольку каждое обращение к процедуре является отдельной сущностью, которая выполняется так же, как если бы она была вызвана из приложения; она резервирует память и стек с учетом требований выполнения ее задач.
Хранимые процедуры
Процедура DEPT_BUDGET в примере базы данных EMPLOYEE иллюстрирует работу рекурсивной процедуры. Она принимает в качестве входа код DNO, эквивалентный коду DEPT_NO - ключу таблицы DEPARTMENT. Таблица DEPARTMENT имеет многоуровневую древовидную структуру: каждый отдел, не являющийся головным отделом, имеет внешний ключ HEAD_DEPT, ссылающийся на DEPT_NO своего непосредственного "родителя".
Процедура обращается к таблице DEPARTMENT по этому полученному ключу. Она сохраняет значение BUDGET этой строки в выходной переменной тот. Она также выполняет подсчет количества отделов, непосредственно предшествующих данному отделу в структуре отделов. Если нет подотделов, оператор EXIT осуществляет переход сразу на финальный оператор END ^. Текущее значение тот становится выходом процедуры, и процедура завершается.
SET TERM ^;
CREATE PROCEDURE DEPT_BUDGET (
DNO CHAR(3) )
RETURNS (
TOT DECIMAL(12,2) )
AS
DECLARE VARIABLE sumb DECIMAL(12, 2);
DECLARE VARIABLE rdno CHAR(3);
DECLARE VARIABLE cnt INTEGER;
BEGIN
tot = 0;
SELECT budget FROM department WHERE dept_no = :dno INTO :tot;
SELECT count(budget) FROM department WHERE head_dept = :dno INTO :cnt;
IF (cnt = 0) THEN
EXIT;
Если существуют подотделы, то выполнение продолжается. Входной код DNO используется в предложении WHERE курсора FOR ... SELECT (см. разд. "Курсоры в PSQL") для выделения по очереди каждой строки из таблицы DEPARTMENT, которая содержит в коде HEAD_DEPT то же значение, что и в DNO, и помещения значение из DEPT_NO этой строки в локальную переменную RDNO:
FOR SELECT dept_no FROM department
WHERE head_dept = :dno
INTO :rdno DO
BEGIN
Эта локальная переменная теперь становится входным кодом для рекурсивного вызова процедуры. При каждой рекурсии выходное значение тот увеличивается на значение возвращаемого бюджета, пока не будут обработаны все соответствующие строки:
EXECUTE PROCEDURE dept_budget :rdno RETURNING_VALUES :sumb;
tot = tot + sumb;
END
Под конец возвращаемое значение, аккумулированное в рекурсиях, передается вызвавшей программе, и процедура завершается:
EXIT; /* оператор EXIT необязателен */
END^
COMMIT^
На этот раз наша процедура имеет входные параметры. Наш простой
EXECUTE PROCEDURE DEPT BUDGET ('600');
Или же мы можем использовать заменяемый параметр:
EXECUTE PROCEDURE DEPT_BUDGET (?);
Курсоры в PSQL
Курсоры состоят из трех основных элементов:
* набора строк, определенных выражением SELECT;
* указателя, который перемещается через набор от первой строки к последней, изолируя строку для некоторого вида деятельности;
* набора переменных- определенных как локальные переменные, выходные аргументы или и те, и другие, - для получения значения столбцов, возвращаемых каждой строкой при ссылке на них указателя.
Проход указателя через набор может рассматриваться как операция "цикла". Операции, выполняющиеся в процессе этого "цикла", когда указатель ссылается на строку, могут быть простыми или сложными.
PSQL имеет две реализации курсора, одна хорошо известная, а другая не была ранее документирована.
* Хорошо известная реализация представлена конструкцией FOR ... SELECT, которая полностью реализует синтаксис цикла и широко используется в процедурах выбора. Она подробно обсуждается в следующем разделе. При этом также допустимо выполнение цикла курсора FOR ... SELECT и внутри выполняемых процедур, как мы видели в предыдущем примере.
* Менее известной реализацией является ранняя реализация, иногда называемая изменяемым или именованным курсором, которая была наследована от ESQL и долгое время была осуждаема. Она представляет объект курсор и позволяет выполнять изменения и удаления. Ее синтаксис будет более понятен тем, кто использовал курсоры в языках процедур в других СУБД. Сейчас мы его вкратце рассмотрим.
! ! !
ПРИМЕЧАНИЕ. Синтаксис и механизм именованного курсора были заново реализованы в процессе разработки Firebird 2. Это краткое описание является лебединой песней старых курсоров, поскольку новый синтаксис вряд ли будет совместимым со старым.
. ! .
Его синтаксис выглядит следующим образом:
. . .
FOR SELECT <список-столбцов>
FROM <имя-таблицы>
FOR UPDATE
INTO <переменные>
AS CURSOR <имя-курсора>
DO
/* либо UPDATE ... */
BEGIN
UPDATE <имя-таблицы>
SET ... WHERE CURRENT OF <имя-курсора>;
. . .
END
/* или DELETE */
BEGIN
DELETE FROM <имя-таблицы>
WHERE CURRENT OF <имя-курсора>
END
. . .
Используйте его сейчас, но ждите изменений в более поздних версиях Firebird. Это очень быстрый способ добавить множество операций в хранимую процедуру, поскольку здесь используется RDB$DB_KEY для локализации записей. RDB$DB_KEY (или просто DBKEY) является внутренней возможностью, которая может быть использована во множестве ситуаций. См. разд. "Темы оптимизации: использование внутреннего RDB$DBKEY".