Циклы for pl sql
На сегодня вы уже многое знаете о PL/SQL. Но очень многое еще впереди. Как и во всех языках программирования, в PL/SQL имеются операторы циклов. Их три основных типа:
- Безусловные циклы (выполняемые бесконечно)
- Интерактивные циклы (FOR)
- Условные циклы (WHILE)
Самый простой тип цикла в языке PL/SQL таков:
LOOP
NULL;
END LOOP
/
Но использовать такой цикл нет смысла, да и для экземпляра БД это не безопасно. Для этого необходимо
использовать определенные пути выхода из циклов. Их то же три:
- EXIT – Безусловный выход из цикла. Используется посредством применения оператора IF.
- EXIT WHEN – Выход при выполнении условия.
- 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 Откуда: | Всем доброго дня! Очень простой вопрос(заранее сорри за ламерство). Есть классический цикл 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). declare |
tru55 Откуда: СПб | В курсорном цикле for после for должно стоять имя record, который определяется неявно как cursor%rowtype (поэтому твоя декларация внутри этого цикла идет лесом) Сообщение было отредактировано: 22 сен 11, 12:01 |
orawish Откуда: Гадюкино-2 (City) | MaXie, потому, что wrong type – вам же парсер сказал. про bulk collect почитайте – возможно, вы пытаетесь придумать именно его |
softwarer Откуда: 127.0.0.1 | Позволю себе расшифровать два предыдущих ответа. Переменная, способная принимать данные из курсора, имеет тип record (запись). В данном случае – запись из одного поля. Вы пытаетесь выполнить операцию присваивания между ней и переменной, имеющей другой тип (в данном случае integer). Эта операция заведомо обречена на провал. Понимание проблемы осложнено тем, что переменная цикла for – это не та переменная, которую Вы определяете в секции declare, пусть она и имеет то же имя. Во вложенных блоках Вы можете определять одноимённые переменные – и именно так и происходит, несмотря на наличие описания переменной valTblNum в секции declare, в цикле for определяется другая переменная valTblNum с типом select%rowtype. То, что Вы хотели написать, пишется в данном случае так: valNum := valTblNum.num; |
MaXie Откуда: | Ни чего не понял! 🙂
Мне не нужен RecordSet. Мне нужна скалярная переменная с типом столбца num таблицы tbl_num, чего я собственно и добиваюсь декларацией:
Я могу объявить курсор по одному полю таблицы и считать последовательно единственное значение каждой его строки в обычную скалярную переменную, тип которой объявлен либо явно, либо неявно через %type -т.е. тип переменной для записи значения курсора не обязательно должен быть объявлен через %rowtype(пример в самом конце).
Да, но я же явно указал в декларации к блоку, что тип переменной valNum является типом переменной valTblNum, т.е. проблем с несовместимостью по типу точно не должно возникнуть.
Переменная valNum объявлена как скалярная переменная. И я пытаюсь значение одной скалярной переменной valTblNum присвоить другой скалярной переменной valNum. Еще раз озвучу мысль, я пытаюсь скалярной переменной valTblNum присвоить очередное значение поля num(значение поля num в очередной строке таблицы tbl_num, которая(строка) выбирается в цикле). Модель такого перебора значений действительно будет работать, если несколько изменить тип “ведущей” переменной valTblNum: declare P.S. Просьба не ссылаться на иные формы обработки значений в цикле, в том числе и с использованием явного механизма курсора – они рабочие, возражений нет! 🙂 declare valNum tbl_num.num%type; |
softwarer Откуда: 127.0.0.1 |
У Вас неправильная постановка вопроса. Правильно так: “Мне не нужны мудрствования, мне нужен результат, чего я собственно и добиваюсь декларациейvalTblNum silver%bullet; |
MaXie Откуда: | >> softwarer Именно так и было – не понял двух предыдущих ответов. Исчерпывающий и предельно доходчивый ответ. Большое спасибо. 🙂 Если возможно, можно попросить все же прокомментировать последний вопрос в рамках данного “обращения”?
Я готов объяснить, что мне так не дает покоя. Если мы объявляем цикл FOR явно: for i in 1..10 Готов принять в качестве ответа краткое: так реализован цикл FOR(“As design!”). 🙂 |
tru55 Откуда: СПб | Почитай PL/SQL Users Guide and Reference Еще раз: после кляузы for в данном типе цикла ДОЛЖНА стоять переменная типа record, чтобы ты там не объявлял в DECLARE – этого требует конструкция (синтаксис). То, что у тебя выбирается только 1 поле – это частный случай, не имеющий отношение к конструкции. Поэтому обращение к полученному значению только так, как указали выше: for R1 in C1 loop var1:= R1.num; R1 C1%ROWTYPE; — эта декларация в случае for происходит неявно exit when C1%NORFOUND; …. CLOSE C1; Сообщение было отредактировано: 22 сен 11, 13:42 |
orawish Откуда: Гадюкино-2 (City) | MaXie, зря вы пытаетесь учить языки программирования бессистемно. |
softwarer Откуда: 127.0.0.1 |
Это один из лучших ответов в данном случае.
Потому что это ни в коей мере не оптимальный подход для случая select. Мне довелось поработать с подобным “оптимальным” решением, и характеристика этого опыта – геморрой на пустом месте. |
MaXie Откуда: | Я понял! Спасибо большое за помощь и терпение! 🙂 |
Ссылка на сообщение
Ссылка (включая название темы)
Ссылка (URL)
x
Источник