Циклы for pl sql

Циклы for pl sql thumbnail

На сегодня вы уже многое знаете о PL/SQL. Но очень многое еще впереди. Как и во всех языках программирования, в PL/SQL имеются операторы циклов. Их три основных типа:

  1. Безусловные циклы (выполняемые бесконечно)
  2. Интерактивные циклы (FOR)
  3. Условные циклы (WHILE)

Самый простой тип цикла в языке PL/SQL таков:

LOOP
NULL;
END LOOP
/

Но использовать такой цикл нет смысла, да и для экземпляра БД это не безопасно. Для этого необходимо
использовать определенные пути выхода из циклов. Их то же три:

  1. EXIT – Безусловный выход из цикла. Используется посредством применения оператора IF.
  2. EXIT WHEN – Выход при выполнении условия.
  3. GOTO – Выход из цикла во внешний контекст.

Давайте рассмотрим пример с применением цикла LOOP EXIT WHEN. Запишем следующее:

DECLARE
i NUMBER := 0;

BEGIN

LOOP — start loop 1
i := i + 1;
IF (i >= 100) THEN
i := 0;
EXIT; — exit when i >= 0
END IF;
END LOOP; — end loop 1

LOOP — start loop 2
i := i + 1;
EXIT WHEN (i >= 100); — exit when i >= 0
END LOOP; — end loop 2——–

END;
/

Получаем после исполнения:

SQL> DECLARE
2 i NUMBER := 0;
3
4 BEGIN
5
6 LOOP
7 i := i + 1;
8 IF (i >= 100) THEN
9 i := 0;
10 EXIT;
11 END IF;
12 END LOOP;
13
14 LOOP
15 i := i + 1;
16 EXIT WHEN (i >= 100);
17 END LOOP;
18
19 END;
20 /

Процедура PL/SQL успешно завершена.

Видимых действий не было, но и ошибок то же! Первый цикл закончился после того как i стало равно 10. При этом оно получило значение 0 и произошел выход из цикла. Второй цикл применил вложенное предложение EXIT WHEN, что является более верным его использованием синтаксически. Тем не менее, применение условных операторов предполагает перед выходом из цикла проделать некоторые действия. Цикл LOOP EXIT WHEN END LOOP в последующем будет самым, часто используемым при построении конструкций курсоров. Рассмотрим еще одну разновидность вышеприведенного цикла:

DECLARE
k NUMBER := 0;

BEGIN

WHILE (k < 10) LOOP
k := k + 1;
END LOOP;

END;
/

Получаем:

SQL> DECLARE
2 k NUMBER := 0;
3
4 BEGIN
5
6 WHILE (k < 10) LOOP
7 k := k + 1;
8 END LOOP;
9
10 END;
11 /

Процедура PL/SQL успешно завершена.

Здесь в отличие от предыдущего цикла, действия выполняются до тех пор пока условие истинно. Если условие ложно, то цикл прекращается. Что хорошо видно из приведенного примера. В PL/SQL в конструкциях циклов нет такого, иногда полезного, оператора как CONTINUE, вследствие того, что выражение CONTINUE зарезервировано языком PL/SQL и используется для других целей. Но такую конструкцию как CONTINUE можно эмулировать, применив цикл вида LOOP EXIT WHEN END LOOP и используя весьма не популярный, но в данном случае очень полезный оператор GOTO!

Запишем следующее, выведем все нечетные числа до 20:

SET SERVEROUTPUT ON

DECLARE
s NUMBER := 0;

BEGIN

DBMS_OUTPUT.enable;

LOOP
IF(MOD(s, 2) = 1) THEN
GOTO LESS;
END IF;
DBMS_OUTPUT.put_line(TO_CHAR(s)||’ is even!’);
<<LESS>>
EXIT WHEN (s = 20);
s := s + 1;
END LOOP;

END;
/

Получаем:

SQL> SET SERVEROUTPUT ON
SQL> DECLARE
2 s NUMBER := 0;
3
4 BEGIN
5
6 DBMS_OUTPUT.enable;
7
8 LOOP
9 IF(MOD(s, 2) = 1) THEN
10 GOTO LESS;
11 END IF;
12 DBMS_OUTPUT.put_line(TO_CHAR(s)||’ is even!’);
13 <<LESS>>
14 EXIT WHEN (s = 20);
15 s := s + 1;
16 END LOOP;
17
18 END;
19 /
0 is even!
2 is even!
4 is even!
6 is even!
8 is even!
10 is even!
12 is even!
14 is even!
16 is even!
18 is even!
20 is even!
0 is even!
2 is even!
4 is even!
6 is even!
8 is even!
10 is even!
12 is even!
14 is even!
16 is even!
18 is even!
20 is even!

Процедура PL/SQL успешно завершена.

В результате применения GOTO в теле цикла получаем не сложную и понятную логику работы. Теперь давайте рассмотрим, не менее полезный и очень популярный в PL/SQL цикл FOR. Он к стати очень удобен при работе с курсорами, но об этом чуть позднее. Запишем следующее:

BEGIN
FOR i IN 1..100 LOOP
NULL;
END LOOP;
END;
/

SQL> BEGIN
2 FOR i IN 1..100 LOOP
3 NULL;
4 END LOOP;
5 END;
6 /

Процедура PL/SQL успешно завершена.

В данный момент FOR успешно ничего не делал аж сто раз! Итак, давайте рассмотрим его чуть ближе, IN как и в операторе SELECT задает диапазон значений итерации цикла, а “..” это как вы помните так называемый “оператор диапазона”! Вот так просто и ясно. Остальное уже знакомо. Замечу так же, что переменная цикла i является переменной только для чтения. По этому шаг цикла FOR изменить принудительно нельзя! Если это необходимо, то лучше применять цикл вида LOOP EXIT WHEN END LOOP! 🙂 Теперь давайте поработаем с числами:

Читайте также:  Что значит нарушение цикла

DECLARE
s NUMBER := 0;

BEGIN
DBMS_OUTPUT.enable;
FOR i IN 1..20 LOOP
IF(MOD(i, 2) = 1) THEN
DBMS_OUTPUT.put_line(TO_CHAR(i)||’ is even!’);
s := i;
END IF;
END LOOP;
DBMS_OUTPUT.put_line(‘last odd number was ‘||TO_CHAR(s));
END;
/

Получаем:

SQL> DECLARE
2 s NUMBER := 0;
3
4 BEGIN
5 DBMS_OUTPUT.enable;
6 FOR i IN 1..20 LOOP
7 IF(MOD(i, 2) = 1) THEN
8 DBMS_OUTPUT.put_line(TO_CHAR(i)||’ is even!’);
9 s := i;
10 END IF;
11 END LOOP;
12 DBMS_OUTPUT.put_line(‘last odd number was ‘||TO_CHAR(s));
13 END;
14 /
1 is even!
3 is even!
5 is even!
7 is even!
9 is even!
11 is even!
13 is even!
15 is even!
17 is even!
19 is even!
last odd number was 19

Процедура PL/SQL успешно завершена.

Та же задачка, только с циклом FOR. Да, функция MOD возвращает остаток от деления чисел, как вы, наверное, уже догадались. Так же в операторе FOR есть возможность задавать обратный отсчет, ну, например, перед стартом или взрывом! 🙂 Вот так:

BEGIN
DBMS_OUTPUT.enable;
FOR i IN REVERSE 1..10 LOOP
DBMS_OUTPUT.put_line(TO_CHAR(i)||’-‘);
END LOOP;
DBMS_OUTPUT.put_line(‘Blastoff!’);
END;
/

Получаем:

SQL> BEGIN
2 DBMS_OUTPUT.enable;
3 FOR i IN REVERSE 1..10 LOOP
4 DBMS_OUTPUT.put_line(TO_CHAR(i)||’-‘);
5 END LOOP;
6 DBMS_OUTPUT.put_line(‘Blastoff!’);
7 END;
8 /
10-
9-
8-
7-
6-
5-
4-
3-
2-
1-
Blastoff!

Процедура PL/SQL успешно завершена.

Нолика не было, но бабахнуло! Вот такой достаточно богатый набор операторов циклов в языке PL/SQL! Надеюсь, вы научитесь с легкостью их применять при построении серверных приложений ваших БД! 🙂

Источник

В этой главе мы обсудим циклы в PL / SQL. Может возникнуть ситуация, когда вам нужно выполнить блок кода несколько раз. В общем случае операторы выполняются последовательно: первый оператор в функции выполняется первым, затем второй и так далее.

Языки программирования предоставляют различные управляющие структуры, которые допускают более сложные пути выполнения.

Оператор цикла позволяет нам выполнять оператор или группу операторов несколько раз, и в большинстве языков программирования ниже приводится общая форма инструкции цикла.

Петлевая архитектура

PL / SQL предоставляет следующие типы циклов для обработки требований циклов. Нажмите на следующие ссылки, чтобы проверить их детали.

S.No Тип и описание петли
1 PL / SQL Basic LOOP

В этой структуре цикла последовательность операторов заключена между операторами LOOP и END LOOP. На каждой итерации выполняется последовательность операторов, а затем управление возобновляется в верхней части цикла.

2 PL / SQL WHILE LOOP

Повторяет оператор или группу операторов, пока данное условие выполняется. Он проверяет условие перед выполнением тела цикла.

3 PL / SQL FOR LOOP

Выполнить последовательность операторов несколько раз и сократить код, который управляет переменной цикла.

4 Вложенные циклы в PL / SQL

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

В этой структуре цикла последовательность операторов заключена между операторами LOOP и END LOOP. На каждой итерации выполняется последовательность операторов, а затем управление возобновляется в верхней части цикла.

Повторяет оператор или группу операторов, пока данное условие выполняется. Он проверяет условие перед выполнением тела цикла.

Выполнить последовательность операторов несколько раз и сократить код, который управляет переменной цикла.

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

Маркировка цикла PL / SQL

Циклы PL / SQL могут быть помечены. Метка должна быть заключена в двойные угловые скобки (<< и >>) и указываться в начале оператора LOOP. Имя метки также может отображаться в конце оператора LOOP. Вы можете использовать метку в операторе EXIT для выхода из цикла.

Следующая программа иллюстрирует концепцию —

DECLARE
i number(1);
j number(1);
BEGIN
<< outer_loop >>
FOR i IN 1..3 LOOP
<< inner_loop >>
FOR j IN 1..3 LOOP
dbms_output.put_line(‘i is: ‘|| i || ‘ and j is: ‘ || j);
END loop inner_loop;
END loop outer_loop;
END;
/

Читайте также:  Цикл с параметром как записывается

Когда приведенный выше код выполняется в командной строке SQL, он дает следующий результат —

i is: 1 and j is: 1
i is: 1 and j is: 2
i is: 1 and j is: 3
i is: 2 and j is: 1
i is: 2 and j is: 2
i is: 2 and j is: 3
i is: 3 and j is: 1
i is: 3 and j is: 2
i is: 3 and j is: 3

PL/SQL procedure successfully completed.

Операторы управления циклом

Операторы управления циклом изменяют выполнение от его нормальной последовательности. Когда выполнение покидает область действия, все автоматические объекты, созданные в этой области, уничтожаются.

PL / SQL поддерживает следующие операторы управления. Маркировка петель также помогает вывести контроль за пределы петли. Нажмите на следующие ссылки, чтобы проверить их детали.

Оператор Exit завершает цикл, и управление передается оператору сразу после END LOOP.

Заставляет петлю пропускать оставшуюся часть своего тела и немедленно проверять свое состояние перед повторением.

Передает управление помеченному выражению. Хотя не рекомендуется использовать оператор GOTO в вашей программе.

Источник

MaXie
Member

Откуда:
Сообщений: 114

Всем доброго дня!
Очень простой вопрос(заранее сорри за ламерство). Есть классический цикл FOR:declare
valTblNum tbl_num.num%type;
valNum valTblNum%type;
begin
for valTblNum in (
select num from tbl_num)
loop
valNum := valTblNum;
end loop;
end;
Выдает ошибку:
ORA-06550:
PLS-00382: expression is of wrong type
ORA-06550:
PL/SQL: Statement ignored

Таблица tbl_num содержит, грубо говоря одно поле num, которое содержит определенную последовательность целых чисел(скажем, от 1 до 6).
Пробуем изменить блок, с целью выяснить, а работает ли сам цикл FOR?

declare
valTblNum tbl_num.num%type;
valNum valTblNum%type;
begin
valNum := ; — фактический тип Integer
for valTblNum in (
select num from tbl_num)
loop
valNum := valNum +1;
dbms_output.put_line(valNum);
end loop;
end;
Получаем список целочисленных значений, соответствующий числу выполненных циклов.
Вопрос: почему в первом случае внутри секции “loop-end loop” нельзя получить значение переменной valTblNum?

tru55
Member

Откуда: СПб
Сообщений: 19790

В курсорном цикле for после for должно стоять имя record, который определяется неявно как cursor%rowtype (поэтому твоя декларация внутри этого цикла идет лесом)

Сообщение было отредактировано: 22 сен 11, 12:01

orawish
Member

Откуда: Гадюкино-2 (City)
Сообщений: 15488

MaXie,

потому, что wrong type – вам же парсер сказал.
вы пытаетесь скалярное значение присвоить массиву (причем целиком массиву – а не элементу)

про bulk collect почитайте – возможно, вы пытаетесь придумать именно его

softwarer
Member

Откуда: 127.0.0.1
Сообщений: 64306
Блог

Позволю себе расшифровать два предыдущих ответа.

Переменная, способная принимать данные из курсора, имеет тип record (запись). В данном случае – запись из одного поля. Вы пытаетесь выполнить операцию присваивания между ней и переменной, имеющей другой тип (в данном случае integer). Эта операция заведомо обречена на провал.

Понимание проблемы осложнено тем, что переменная цикла for – это не та переменная, которую Вы определяете в секции declare, пусть она и имеет то же имя. Во вложенных блоках Вы можете определять одноимённые переменные – и именно так и происходит, несмотря на наличие описания переменной valTblNum в секции declare, в цикле for определяется другая переменная valTblNum с типом select%rowtype.

То, что Вы хотели написать, пишется в данном случае так:

valNum := valTblNum.num;

MaXie
Member

Откуда:
Сообщений: 114

Ни чего не понял! 🙂

автор
В курсорном цикле for после for должно стоять имя record

Мне не нужен RecordSet. Мне нужна скалярная переменная с типом столбца num таблицы tbl_num, чего я собственно и добиваюсь декларацией:
valTblNum tbl_num.num%type;
Назначение этой переменной – сохранение одного из значений столбаца num. Предполагается, что в цикле FOR последовательно в переменую valTblNum будут считаны все значения этого столбца и в цикле обработаны.

автор
который определяется неявно как cursor%rowtype

Я могу объявить курсор по одному полю таблицы и считать последовательно единственное значение каждой его строки в обычную скалярную переменную, тип которой объявлен либо явно, либо неявно через %type -т.е. тип переменной для записи значения курсора не обязательно должен быть объявлен через %rowtype(пример в самом конце).

автор
потому, что wrong type – вам же парсер сказал.

Да, но я же явно указал в декларации к блоку, что тип переменной valNum является типом переменной valTblNum, т.е. проблем с несовместимостью по типу точно не должно возникнуть.

автор
вы пытаетесь скалярное значение присвоить массиву (причем целиком массиву – а не элементу)

Переменная valNum объявлена как скалярная переменная. И я пытаюсь значение одной скалярной переменной valTblNum присвоить другой скалярной переменной valNum.

Еще раз озвучу мысль, я пытаюсь скалярной переменной valTblNum присвоить очередное значение поля num(значение поля num в очередной строке таблицы tbl_num, которая(строка) выбирается в цикле). Модель такого перебора значений действительно будет работать, если несколько изменить тип “ведущей” переменной valTblNum:

declare
valTblNum tbl_num%rowtype;
valNum valTblNum.num%type;
begin
for valTblNum in (
select * from tbl_num)
loop
valNum := valTblNum.num;
end loop;
end;
Но вопрос остается вопросом: почему в цикле FOR нельзя ограничиться набором значений(массивом), и необходимо использовать исключительно весь набор значений(DataSet) таблицы, из которого затем, уже в теле обработки, осуществить выбор необходимого поля для значений?

P.S. Просьба не ссылаться на иные формы обработки значений в цикле, в том числе и с использованием явного механизма курсора – они рабочие, возражений нет! 🙂

declare
type t_ref_cursor is ref cursor;
ref_c t_ref_cursor;

valNum tbl_num.num%type;
begin
open ref_c for
select num from tbl_num;
loop
fetch ref_c into valInt;
exit when ref_c%notfound;
end loop;
close ref_c;
end;

Читайте также:  Разница между рекурсией и циклом

softwarer
Member

Откуда: 127.0.0.1
Сообщений: 64306
Блог

MaXie
Мне не нужен RecordSet. Мне нужна скалярная переменная с типом столбца num таблицы tbl_num, чего я собственно и добиваюсь декларацией:

У Вас неправильная постановка вопроса. Правильно так: “Мне не нужны мудрствования, мне нужен результат, чего я собственно и добиваюсь декларациейvalTblNum silver%bullet;

MaXie
Member

Откуда:
Сообщений: 114

>> softwarer

Именно так и было – не понял двух предыдущих ответов. Исчерпывающий и предельно доходчивый ответ. Большое спасибо. 🙂

Если возможно, можно попросить все же прокомментировать последний вопрос в рамках данного “обращения”?

автор
Но вопрос остается вопросом: почему в цикле FOR нельзя ограничиться набором значений(массивом), и необходимо использовать исключительно весь набор значений(DataSet) таблицы, из которого затем, уже в теле обработки, осуществить выбор необходимого поля для значений?

Я готов объяснить, что мне так не дает покоя. Если мы объявляем цикл FOR явно:

for i in 1..10
loop

end loop;
то используется массив значений. Почему нельзя использовать столь же оптимальный подход и в диапазоне значений, объявляемом через конструкцию “select”?

Готов принять в качестве ответа краткое: так реализован цикл FOR(“As design!”). 🙂

tru55
Member

Откуда: СПб
Сообщений: 19790

Почитай PL/SQL Users Guide and Reference

Еще раз: после кляузы for в данном типе цикла ДОЛЖНА стоять переменная типа record, чтобы ты там не объявлял в DECLARE – этого требует конструкция (синтаксис). То, что у тебя выбирается только 1 поле – это частный случай, не имеющий отношение к конструкции. Поэтому обращение к полученному значению только так, как указали выше:

for R1 in C1 loop

var1:= R1.num;
end loop;

Если бы ты использовал другую форму работы с курсором, то это выглядело бы так:
DECLARE
CURSOR C1 — кстати, я предпочитаю всегда объявлять курсор в DECLARE независимо от того, какая форма используется
IS
SELECT ….

R1 C1%ROWTYPE; — эта декларация в случае for происходит неявно
BEGIN
OPEN C1;
loop
FETCH C1 INTO R1;

exit when C1%NORFOUND;

….
end loop;

CLOSE C1;
END;

Сообщение было отредактировано: 22 сен 11, 13:42

orawish
Member

Откуда: Гадюкино-2 (City)
Сообщений: 15488

MaXie,

зря вы пытаетесь учить языки программирования бессистемно.
1.читайте основные концепции. ваши вопросы (насколько я понимаю, возникшие из непонимания нотации) проясните уже тут.
2.гайды собственно по pl/sql (ну и sql оракловому – куда же без него)

softwarer
Member

Откуда: 127.0.0.1
Сообщений: 64306
Блог

MaXie
Готов принять в качестве ответа краткое: так реализован цикл FOR(“As design!”). 🙂

Это один из лучших ответов в данном случае.

MaXie
Почему нельзя использовать столь же оптимальный подход и в диапазоне значений, объявляемом через конструкцию “select”?

Потому что это ни в коей мере не оптимальный подход для случая select. Мне довелось поработать с подобным “оптимальным” решением, и характеристика этого опыта – геморрой на пустом месте.

MaXie
Member

Откуда:
Сообщений: 114

Я понял!
Спасибо большое за помощь и терпение! 🙂

Источник