Шаг 49 - PL/SQL - виды и типы циклов

На сегодня вы уже многое знаете о 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! Надеюсь, вы научитесь с легкостью их применять при построении серверных приложений ваших БД! :)


Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Летучий Сергей - 21.10.2003