Циклы в PL/SQL
Циклы используются для повторения какого-либо действия несколько раз.
Loop
Простейший цикл в PL/SQL выглядит так:
loop
-- какое-либо действие
end loop;Данный цикл будет выполнять код между loop и end loop бесконечно. Для того, чтобы завершить цикл, можно использовать команду exit:
declare
i number := 5;
begin
loop
if i = 0 then
exit;
end if;
dbms_output.put_line(i);
i := i - 1;
end loop;
end;Результат:
5
4
3
2
1Переменная i отвечает за оставшееся количество повторений. Такие переменные в программировании ещё называют счётчиками цикла.
Для более удобного выхода из цикла можно использовать конструкцию exit when:
declare
i number := 5;
begin
-- Такой же цикл, как и в предыдущем примере, только
-- читается гораздо лучше
loop
exit when i = 0;
dbms_output.put_line(i);
i := i - 1;
end loop;
end;Exit можно использовать только внутри цикла, внутри обычного блока его использование приведёт к ошибке:
-- Ошибка!
begin
exit;
end;For
Цикл for удобно использовать для автоматического изменения счётчика цикла:
begin
for i in 1..5
loop
dbms_output.put_line(i);
end loop;
end;Результат:
1
2
3
4
5Здесь не использовались ни проверки на необходимость выйти из цикла, ни изменение счётчика цикла - конструкция for всё сделала за нас. Мы обращаемся к переменной i - она видима только внутри тела цикла, и недоступна вне его.
В for указывается нижняя граница счётчика цикла, и верхняя. Изменение производится увеличением счётчика на единицу. Нижняя граница всегда должна быть меньше либо равной верхней границе:
-- Выведет:
-- -5
-- -4
-- -3
-- -2
-- -1
-- 0
-- 1
begin
for i in -5..1
loop
dbms_output.put_line(i);
end loop;
end;Если верхняя граница равна нижней, цикл выполнится ровно один раз:
-- Выведет одну строку:
-- 1
begin
for i in 1..1
loop
dbms_output.put_line(i);
end loop;
end;В сторону уменьшения работать не будет - тело цикла for i in 3..1 не выполнится ни разу.
Стоит также обратить внимание, что мы не объявляли переменную i в секции declare - она создаётся автоматически. Более того, если мы объявим переменную вне цикла for, то после завершения цикла внешняя переменная останется без изменений:
declare
i number := 10;
begin
for i in 1..4
loop
null;
end loop;
-- Выведет 10, а не 4, как можно было бы подумать
dbms_output.put_line(i);
end;While
Цикл while будет работать до тех пор, пока условие, указанное после него, истинно:
declare
i number := 5;
begin
while i > 0
loop
dbms_output.put_line(i);
i := i -1;
end loop;
end;Вложенные циклы
Циклы могут быть вложенными. В целом, здесь нет ничего сложного, обратим мнимание лишь на несколько моментов.
Вложенные циклы for могут иметь счётчики с одним и тем же именем:
begin
for i in 1..2
loop
dbms_output.put_line('i1= ' || i);
for i in 1..2
loop
dbms_output.put_line('i2= ' || i);
end loop;
end loop;
end;Выведет:
i1= 1
i2= 1
i2= 2
i1= 2
i2= 1
i2= 2Как видно, значения счётчика из внешнего цикла не перепуталось со значением счётчика из внутреннего. Это произошло потому, что мы обращались к счётчикам внутри тела цикла, к которому они относятся. Если мы перенесём вывод счётчика внешнего цикла внутрь тела внутреннего, работать не будет:
begin
for i in 1..2
loop
for i in 1..2
loop
dbms_output.put_line('i1= ' || i);
dbms_output.put_line('i2= ' || i);
end loop;
end loop;
end;i1= 1
i2= 1
i1= 2 -- А должно быть 1
i2= 2
i1= 1
i2= 1
i1= 2
i2= 2Решить эту проблему помогают метки блоков:
begin
<<main>>
for i in 1..2
loop
<<child>>
for i in 1..2
loop
dbms_output.put_line('i1= ' || main.i);
dbms_output.put_line('i2= ' || child.i);
end loop;
end loop;
end;Результат:
i1= 1
i2= 1
i1= 1
i2= 2
i1= 2
i2= 1
i1= 2
i2= 2Синтаксис для обращения к переменным с использованием меток выглядит так: имя метки.переменная. При использовании вложенных циклов часто используют отдельный вариант синтаксиса завершения цикла, чтобы было понятнее, какой именно цикл заканчивает свою работу:
begin
<<main>>
for i in 1..2
loop
<<child>>
for i in 1..2
loop
dbms_output.put_line('i1= ' || main.i);
dbms_output.put_line('i2= ' || child.i);
-- указываем имя идентификатора блока
end loop child;
end loop main;
end;Continue. Переход на следующую итерацию цикла
Команда continue используется для перехода к следующей итерации цикла. Часть тела цикла после данной команды не будет выполнена.
-- Выведет пять строк с числами от 1 до 5.
-- Числа с приплюсованной двойкой не будут выведены,
-- так как их вывод находится после continue.
begin
for i in 1..5
loop
dbms_output.put_line(i);
continue;
dbms_output.put_line(i + 2);
end loop;
end;Как и с командой exit, есть и более удобный вариант этой команды - continue when:
begin
for i in 1..10
loop
continue when i mod 2 = 0;
dbms_output.put_line(i);
end loop;
end;Результат:
1
3
5
7
9В начале каждой итерации мы проверяем остаток от деления на два, и если он равен нулю, сразу переходи к следующей итерации, таким образом выводя только нечётные числа.