Шаг 61 - PL/SQL - КУРСОРЫ - циклы и выборка данных

Итак, немного разобравшись с неявными курсорами приступим вплотную к изучению явных курсоров. Если кто из вас был внимателен, и в шаге 54 заметил, что одна из строк в последнем курсоре с циклом выборки выдалась дважды! А произошло это по той причине, что оператор FETCH с одними данными сработал дважды. Кстати на досуге можете подумать над тем как устранить этот недочет. А, пока давайте поучимся писать курсоры с циклами. Для работы будем использовать таблицу OFFICES. Для начала запишем вот такой блок:

SET SERVEROUTPUT ON

DECLARE
	
	CURSOR get_offices IS
		SELECT * FROM OFFICES;

	v_gt get_offices%ROWTYPE;

BEGIN
	
	DBMS_OUTPUT.enable;

	OPEN get_offices; 

	LOOP 

		DBMS_OUTPUT.put_line('Get Data: '||TO_CHAR(v_gt.OFFICE)||' '||v_gt.CITY||' '
		||v_gt.REGION||' '||TO_CHAR(v_gt.MGR)||' '||TO_CHAR(v_gt.TARGET)||' '||TO_CHAR(v_gt.SALES));

		FETCH get_offices INTO v_gt;			
		EXIT WHEN get_offices%NOTFOUND;

	END LOOP;
			
	CLOSE get_offices; 
	
END;
/

После прохода в SQL*Plus получаем:

SQL> SET SERVEROUTPUT ON
SQL>
SQL> DECLARE
  2  
  3  	CURSOR get_offices IS
  4  		SELECT * FROM OFFICES;
  5  
  6  	v_gt get_offices%ROWTYPE;
  7  
  8  BEGIN
  9  
 10  	DBMS_OUTPUT.enable;
 11  
 12  	OPEN get_offices;
 13  
 14  	LOOP
 15  
 16   		DBMS_OUTPUT.put_line('Get Data: '||TO_CHAR(v_gt.OFFICE)||' '||v_gt.CITY||' '
 17  		||v_gt.REGION||' '||TO_CHAR(v_gt.MGR)||' '||TO_CHAR(v_gt.TARGET)||' '||TO_CHAR(v_gt.SALES));
 18  
 19  		FETCH get_offices INTO v_gt;
 20  		EXIT WHEN get_offices%NOTFOUND;
 21  
 22  	END LOOP;
 23  
 24  	CLOSE get_offices;
 25  
 26  END;
 27  /
Get Data:                                                                       
Get Data: 22 Запиндрищинск Запад 108 300 186,042                                
Get Data: 11 Красный Мотоцикл Восток 106 575 692,637                            
Get Data: 12 Чугуевск Восток 104 800 735,044                                    
Get Data: 13 Бубурино Восток 105 350 367,911                                    
Get Data: 21 Котрогайка Запад 108 725 835,915                                   

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

Опять хорошо видно, что первая строка пустая. Но все по порядку. Использовали стандартный цикл LOOP, как правило это самый распространенный подход при написании курсора. А первая строка, пустая вследствие того, что put_line отработал с пустыми переменными. Этот недочет можно исправить, вынеся первый оператор FETCH за тело цикла вот так:

DECLARE
	
	CURSOR get_offices IS
		SELECT * FROM OFFICES;

	v_gt get_offices%ROWTYPE;

BEGIN

	DBMS_OUTPUT.enable;

	OPEN get_offices;

	FETCH get_offices INTO v_gt;			

	LOOP 

		DBMS_OUTPUT.put_line('Get Data: '||TO_CHAR(v_gt.OFFICE)||' '||v_gt.CITY||' '
		||v_gt.REGION||' '||TO_CHAR(v_gt.MGR)||' '||TO_CHAR(v_gt.TARGET)||' '||TO_CHAR(v_gt.SALES));
				
		FETCH get_offices INTO v_gt;			
		EXIT WHEN get_offices%NOTFOUND;

	END LOOP;
			
	CLOSE get_offices; 

END;
/

Получаем:

SQL> DECLARE
  2  
  3  	CURSOR get_offices IS
  4  		SELECT * FROM OFFICES;
  5  
  6  	v_gt get_offices%ROWTYPE;
  7  
  8  BEGIN
  9  
 10  	DBMS_OUTPUT.enable;
 11  
 12  	OPEN get_offices;
 13  
 14  	FETCH get_offices INTO v_gt;
 15  
 16  	LOOP
 17  
 18  		DBMS_OUTPUT.put_line('Get Data: '||TO_CHAR(v_gt.OFFICE)||' '||v_gt.CITY||' '
 19  		||v_gt.REGION||' '||TO_CHAR(v_gt.MGR)||' '||TO_CHAR(v_gt.TARGET)||' '||TO_CHAR(v_gt.SALES));
 20  
 21  		FETCH get_offices INTO v_gt;
 22  		EXIT WHEN get_offices%NOTFOUND;
 23  
 24  	END LOOP;
 25  
 26  	CLOSE get_offices;
 27  
 28  END;
 29  /
Get Data: 22 Запиндрищинск Запад 108 300 186,042                                
Get Data: 11 Красный Мотоцикл Восток 106 575 692,637                            
Get Data: 12 Чугуевск Восток 104 800 735,044                                    
Get Data: 13 Бубурино Восток 105 350 367,911                                    
Get Data: 21 Котрогайка Запад 108 725 835,915                                   

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

Вот теперь количество строк точно соответствует количеству записей в таблице. Если не устраивает, можете придумать более оригинальный способ! :) Вот так строится классический курсор с циклом LOOP. Давайте сделаем выборку циклом WHILE и посмотрим, что получится. Запишем следующий блок:

SET SERVEROUTPUT ON

DECLARE
	
	CURSOR get_offices IS
		SELECT * FROM OFFICES;

	v_gt get_offices%ROWTYPE;

BEGIN

	DBMS_OUTPUT.enable;

	OPEN get_offices; 

	FETCH get_offices INTO v_gt;

	WHILE (get_offices%FOUND) LOOP 

		DBMS_OUTPUT.put_line('Get Data: '||TO_CHAR(v_gt.OFFICE)||' '||v_gt.CITY||' '
		||v_gt.REGION||' '||TO_CHAR(v_gt.MGR)||' '||TO_CHAR(v_gt.TARGET)||' '||TO_CHAR(v_gt.SALES));

		FETCH get_offices INTO v_gt;					

	END LOOP;

	CLOSE get_offices; 

END;
/

После прохода в SQL*Plus получаем:

SQL> DECLARE
  2  
  3  	CURSOR get_offices IS
  4  		SELECT * FROM OFFICES;
  5  
  6  	v_gt get_offices%ROWTYPE;
  7  
  8  BEGIN
  9  
 10  	DBMS_OUTPUT.enable;
 11  
 12  	OPEN get_offices;
 13  
 14  	FETCH get_offices INTO v_gt;
 15  
 16  	WHILE (get_offices%FOUND) LOOP
 17  
 18  		DBMS_OUTPUT.put_line('Get Data: '||TO_CHAR(v_gt.OFFICE)||' '||v_gt.CITY||' '
 19  		||v_gt.REGION||' '||TO_CHAR(v_gt.MGR)||' '||TO_CHAR(v_gt.TARGET)||' '||TO_CHAR(v_gt.SALES));
 20  
 21  		FETCH get_offices INTO v_gt;
 22  
 23  	END LOOP;
 24  
 25  	CLOSE get_offices;
 26  
 27  END;
 28  /
Get Data: 22 Запиндрищинск Запад 108 300 186,042                                
Get Data: 11 Красный Мотоцикл Восток 106 575 692,637                            
Get Data: 12 Чугуевск Восток 104 800 735,044                                    
Get Data: 13 Бубурино Восток 105 350 367,911                                    
Get Data: 21 Котрогайка Запад 108 725 835,915                                   

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

А, вот здесь выноска первого FETCH за тело цикла, просто необходима по определению, иначе сам цикл WHILE будет трудно заставить работать. В дальнейшем я бы рекомендовал не использовать данный цикл для работы с курсором вследствие его неудобства. Далее, вы поймете, что цикл LOOP наиболее применим и еще цикл FOR, но это уже отдельная тема. Пока можете поработать с этими типами и попробовать устранить мои ошибки! Удачи!


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