Самоучитель по SQL-сервер в Linux

         

Циклы

Другую категорию команд, передающих управление внутри функций, составляют циклы. В циклах используются разные виды итераций, предназначенные для решения разных задач. Итеративные вычисления значительно расширяют возможности функций PL/pgSQL.

В PL/pgSQL реализованы три типа циклов: простейший (безусловный) цикл, цикл WHILE и цикл FOR. Вероятно, из этих трех циклов чаще всего применяется цикл FOR, подходящий для широкого круга задач программирования, хотя и другие циклы также достаточно часто встречаются на практике.

Безусловный цикл

Ключевое слово LOOP начинает простейший безусловный цикл. Команды безусловного цикла выполняются до тех пор, пока не будет достигнуто ключевое слово EXIT. За ключевым словом EXIT может следовать секция WHEN с выражением, определяющим условие выхода. Выражение должно относиться к логическому типу. Например, оно может проверять, достигла ли переменная некоторой величины. Ниже приведен синтаксис безусловного цикла (без ключевого слова LOOP):

LOOP



команде:

[...]

END LOOP:

Команда EXIT завершает работу безусловного цикла и может дополнительно содержать метку и/или условие завершения.

Метка представляет собой произвольный идентификатор, заключенный между префиксом « и суффиксом ». Чтобы назначить метку циклу, следует расположить ее непосредственно перед началом цикла. Синтаксис определения цикла с меткой:

«метка»

LOOP

[...]

END LOOP:

Назначение метки циклу позволяет указать нужный цикл при выходе из нескольких вложенных циклов (команда EXIT с меткой работает лишь в том случае, если завершаемому циклу была присвоена соответствующая метка).

Если команда EXIT содержит условие, цикл прерывается только в том случае, если это условие истинно.

Синтаксис вызова EXIT в цикле LOOP:

[ «метка» ]

LOOP

statement;

[...]

EXIT [ метка ] [ WHEN условие ]:

END LOOP:

В листинге 11.42 приведен пример безусловного цикла и команды EXIT, завершающей цикл при выполнении некоторого условия. Функция square_i ntegerj oop() возводит целое число в квадрат (умножает его само на себя) до тех пор, пока его значение не превысит 10 000, после чего возвращает полученный результат.

Листинг 11.42. Использование безусловного цикла

CREATE FUNCTION square_integer_loop (integer) RETURNS integer AS '

DECLARE

-- Объявление псевдонима для аргумента,

numl ALIAS FOR $1;

- Объявление целочисленной переменной для хранения результата,

result integer;

BEGIN

- Исходное число присваивается переменной

result, result := numl;

LOOP

result := result * result:

EXIT WHEN result >= 10000;

END LOOP;

RETURN result:

END; '

LANGUAGE 'plpgsql';

В листинге 11.43 показан результат вызова square_i nteger_l oop() с аргументом 3.

Листинг 11.43. Результат вызова функции square_integer_loop()

booktown=# SELECT square_integer_loop(3);

squa re_i nteger_l oop

6561

(1 row)

Цикл WHILE

Цикл WHILE выполняет блок команд до тех пор, пока заданное условие не станет ложным. При каждой итерации цикла WHILE условие проверяется перед выполнением первой команды блока, и если условие равно TRUE — блок выполняется. Таким образом, если условие никогда не становится равным FALSE, блок выполняется в бесконечном цикле вплоть до принудительного завершения клиентского процесса. Синтаксис цикла WHILE:

[ «метка» ]

WHILE условие LOOP

команда:

[...]

END LOOP;

В листинге 11.44 циклы WHILE продемонстрированы на примере функции add_two_loop(). Функция увеличивает число на 1 до тех пор, пока не будет достигнуто некоторое пороговое значение. Начальное и конечное значения передаются функции в виде аргументов. Обозначение !=, встречающееся в листинге 11.44, является оператором неравенства. В данном примере условие означает следующее: цикл WHILE продолжает выполняться, пока переменная result не равна переменной highjiumber. Иначе говоря, цикл WHILE завершается в тот момент, когда переменная result становится равной highjiumber.

Листинг 11.44. Использование цикла WHILE

CREATE FUNCTION add_two_loop (integer, integer) RETURNS integer AS '

DECLARE

-- Объявление псевдонимов для аргументов.

low_number ALIAS FOR $1: highjiumber ALIAS FOR $2:

-- Объявление переменной для хранения результата,

result integer = 0:

BEGIN

-- Увеличивать переменную result на 1 до тех пор.

-- пока она не станет равна high_number.

WHILE result != highjiumber LOOP

result := result + 1:

END LOOP:

RETURN result;

END:

' LANGUAGE 'plpgsql';

Цикл FOR

Возможно, циклы FOR — самая важная разновидность циклов, реализованных в PL/ pgSQL. Цикл FOR выполняет программный блок для целых чисел из заданного интервала. У циклов FOR в PL/pgSQL существуют аналоги в других процедурных языках программирования (например, С).

Заголовок цикла FOR начинается с объявления целочисленной переменной, управляющей выполнением цикла. Затем указывается интервал принимаемых ею значений, а далее следует блок команд. Управляющая переменная уничтожается сразу же после выхода из цикла, причем ее не нужно объявлять в секции объявлений блока. Синтаксис цикла FOR:

[ «метке» ]

FOR переменная IN [ REVERSE ] выражение! . . выражение? LOOP

команда:

[...]

END LOOP:

Цикл FOR выполняет одну итерацию для каждого значения переменной переменная в интервале, границы которого определяются выражениями выражение! и выражекие2 (включительно). В начале цикла переменная инициализируется значением выражения выражение! и увеличивается на 1 после каждой итерации. Если в заголовке цикла присутствует ключевое слово REVERSE, то переменная не увеличивается, а уменьшается.

ПРИМЕЧАНИЕ

Управляющую переменную цикла не обязательно объявлять вне блока FOR, если вы не собираетесь работать с ней после завершения цикла.

Циклы FOR также используются для перебора результатов запросов. Пример приведен в листинге 11.45, где цикл FOR работает с переменными RECORD и &ROWTYPE. Синтаксис цикла FOR с перебором записей:

[ «метка» ]

FOR { переменная_record %пврененная_гоы1уре } IN xonaHaa_select LOOP

команда :

[...]

END LOOP:

В листинге 11.45 функция extract_all_titles() получает из базы данных список всех названий книг, упорядоченных по теме. Если по какой-либо теме в базе данных не находится ни одной книги, выводится пустая строка. Список возвращается в виде текстовой переменной. Перебор тем по кодам в функции extract_a1I_tit1es() осуществляется в цикле FOR.

Внутри первого цикла FOR находится другой, вложенный цикл FOR. Он перебирает все книги в базе данных и отбирает те из них, у которых поле subjectj d совпадает с управляющей переменной исходного цикла (текущим кодом темы). В листинге 11.45 управляющая переменная i инициализируется нулевым значением, поскольку нумерация кодов тем в таблице subjects начинается с 0.

Листинг 11.45. Пример использования цикла FOR

CREATE FUNCTION extract_all __titles2 () RETURNS text AS '

DECLARE

-- Объявление переменной для кода темы. sub_id integer;

-- Объявление переменной для хранения списка названий книг.

text_output text = :

-- Объявление переменной для названия темы.

sub_title text;

-- Объявление переменной для хранения записей.

-- полученных при выборке из таблицы books.

row_data booksSSROWTYPE: BEGIN

-- Внешний цикл FOR: тело цикла выполняется до тех пор.

-- пока переменная 1 не станет равна 15. Перебор начинается с 0.

-- Следовательно, тело цикла будет выполнено 16 раз

-- (по одному пля каждой темы).

FOR i IN 0..15 LOOP

-- Получить из таблицы subjects название темы.

-- код которой совпадает со значением переменной 1.

SELECT INTO sub_title subject FROM subjects WHERE id = 1:

-- Присоединить название темы, двоеточие и символ новой строки

-- к переменной text_output.

text_output = text_output || "\n" | sub_title | ":\n";

-- Перебрать все записи таблицы books.

-- у которых код темы совпадает со значением переменной 1.

FOR row_data IN SELECT * FROM books

WHERE subjectjd = i LOOP

-- Присоединить к переменной text_output название книги

-- и символ новой строки.

text_output := text_output || row_data.title || "\n":

END LOOP;

END LOOP:

-- Вернуть список.

RETURN text_output;

END:

' LANGUAGE 'plpgsql':

В листинге 11.46 приведена другая функция, в которой цикл FOR используется для перебора результатов запроса SQL. При каждой итерации цикла FOR в листинге 11.46 содержимое одной из записей запроса к таблице books помещается в переменную row_data, после чего значение поля title присваивается переменной text_output.

Цикл продолжается до тех пор, пока не будет достигнута последняя запись в таблице books. В конце цикла переменная text_output содержит полный список всех книг по теме, код которой был передан в аргументе функции. Работа функции завершается возвращением переменной text_output.

Листинг 11.46. Использование цикла FOR с атрибутом %ROWTYPE

CREATE FUNCTION extract_title (integer) RETURNS text AS '

DECLARE

-- Объявление псевдонима для аргумента функции,

subjd ALIAS FOR $1:

-- Объявление переменной для хранения названий книг.

-- Переменная инициализируется символом новой строки,

text output text : = ''\n'';

-- Обьявление переменной для хранения записей

-- таблицы books, row data booksXROWTYPE:

BEGIN

-- Перебор результатов запроса.

FOR rowjata IN SELECT * FROM books

WHERE subjectjd = subjd ORDER BY title LOOP

-- Присоединить название книги к переменной text_output.

text_output := text_output || row_data.title || "\n";

END LOOP:

-- Вернуть список книг.

RETURN text_output:

END:

' LANGUAGE 'plpgsql':

В листинге 11.47 показан результат вызова функции extract_title() с аргументом 2. В таблице subjects этот код соответствует теме «Children's Books» (книги для детей).

Листинг 11.47. Результат выполнения функции extract_title()

booktown=# SELECT extract_title(2);

extract_title

Bartholomew and the Oobleck

Franklin in the Dark

Goodnight Moon

The Cat in the Hat

(1 row)

Переменная row_data объявляется с атрибутом UROWTYPE no отношению к таблице books, поскольку она будет использоваться только для хранения записей из таблицы books. С таким же успехом можно было объявить row_data с типом RECORD:

rowjata RECORD:

Впрочем, это следует делать только в том случае, если в переменной предполагается хранить записи из нескольких таблиц.

Функция extract_ti tl e() возвращает одинаковые результаты как при объявлении переменной с типом RECORD, так и с атрибутом &ROWTYPE.




Содержание раздела