Шаг 54 - PL/SQL - КУРСОР - извлекаем данные, оператор FETCH

С определениями, открытиями и закрытиями мы разобрались. Давайте, наконец, получим какие-нибудь результаты, т.е. данные. Собственно для чего и нужны курсоры. Выборка данных из курсора производится с помощью оператора FETCH. Пожалуй, он является ключевой фигурой в данном случае. Так как именно с его помощью происходит выборка из активного набора, сформированного при открытии курсора. После того как курсор открыт, данные в контекстной области SGA уже готовы, их остается только извлечь. Оператор FETCH именно это и производит. Синтаксис его таков:

---------- FETCH - имя курсора - INTO - список переменных --------------
и
---------- FETCH - имя курсора - INTO - запись PL/SQL (%ROWTYPE) --------

Итак, переходим к практическим действиям. Запускаем наш старый добрый SQL*Plus. Запишем вот такой блок, используя пройденный материал:

DECLARE
	
	v_Office OFFICES.OFFICE%TYPE;
	v_City OFFICES.CITY%TYPE;
	
	CURSOR get_offices IS
		SELECT OFFICE, CITY 
		FROM OFFICES;

BEGIN

	OPEN get_offices; 
		-- Use operator FETCH to get variables!
		FETCH get_offices INTO v_Office, v_City;	
	
	CLOSE get_offices; 
	
END;
/

Получаем следующее:

SQL> DECLARE
  2  
  3   v_Office OFFICES.OFFICE%TYPE;
  4   v_City OFFICES.CITY%TYPE;
  5  
  6   CURSOR get_offices IS
  7       SELECT OFFICE, CITY
  8       FROM OFFICES;
  9  
 10  BEGIN
 11  
 12   OPEN get_offices;
 13    -- Use operator FETCH to get variables!
 14    FETCH get_offices INTO v_Office, v_City;
 15  
 16   CLOSE get_offices;
 17  
 18  END;
 19  /

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

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

SET SERVEROUTPUT ON

DECLARE
	
	v_Office OFFICES.OFFICE%TYPE;
	v_City OFFICES.CITY%TYPE;
	
	CURSOR get_offices IS
		SELECT OFFICE, CITY 
		FROM OFFICES;

BEGIN

	OPEN get_offices; 
		-- Use operator FETCH to get variables!
		FETCH get_offices INTO v_Office, v_City;	
		
		DBMS_OUTPUT.enable;
		DBMS_OUTPUT.put_line('OutPutString is: '||TO_CHAR(v_Office)||' '||v_City);
	CLOSE get_offices; 
	
END;
/

Получаем следующее:

SQL> SET SERVEROUTPUT ON

SQL> DECLARE
  2  
  3	v_Office OFFICES.OFFICE%TYPE;
  4	v_City OFFICES.CITY%TYPE;
  5  
  6	CURSOR get_offices IS
  7		SELECT OFFICE, CITY
  8		FROM OFFICES;
  9  
 10	BEGIN
 11  
 12	OPEN get_offices;
 13		-- Use operator FETCH to get variables!
 14		FETCH get_offices INTO v_Office, v_City;
 15  
 16		DBMS_OUTPUT.enable;
 17		DBMS_OUTPUT.put_line('OutPutString is: '||TO_CHAR(v_Office)||' '||v_City);
 18  
 19	CLOSE get_offices;
 20  
 21	END;
 22	/
OutPutString is: 22 Запиндрищинск                                               

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

Ура! Наконец-то мы видим результат! Давайте подробнее рассмотрим, что же произошло. Поле открытия курсора (не параметризованного) происходит выборка данных, согласно выражения SELECT курсора. Кстати легко проверить, что должен вернуть курсор просто выполнив его оператор SELECT, вот так:

SELECT OFFICE, CITY 
	FROM OFFICES
/

Получаем:

SQL> SELECT OFFICE, CITY
  2     FROM OFFICES
  3  /
  
 OFFICE CITY
------- -------------------
     22 Запиндрищинск
     11 Красный Мотоцикл
     12 Чугуевск
     13 Бубурино
     21 Котрогайка

5 строк выбрано

Это и есть ваш результирующий набор! Но здесь пять(!) строк скажете вы! Да! В таблице OFFICES пять записей, как вы помните из наших прошлых занятий. Но пример с FETCH вернул одну строку, потому что этот оператор производит выбор одной строки и смещает указатель в контекстной области на единицу. И ждет следующей команды на выборку. До тех пор, пока не будет достигнута последняя запись. Когда будет достигнута последняя запись, сработает атрибут курсора %NOTFOUND (его мы рассмотрим чуть позднее), он станет TRUE. Это значит, что все записи из результирующего набора выбраны! Но с помощью нашего примера можно выбрать и все пять записей, сделав вот так:

SET SERVEROUTPUT ON

DECLARE

	v_Office OFFICES.OFFICE%TYPE;
	v_City OFFICES.CITY%TYPE;
	
	CURSOR get_offices IS
		SELECT OFFICE, CITY 
		FROM OFFICES;

BEGIN

	OPEN get_offices; 

		DBMS_OUTPUT.enable;
		-- Use operator FETCH to get variables!
		FETCH get_offices INTO v_Office, v_City;	-- 1
		DBMS_OUTPUT.put_line('OutPutString is: '||TO_CHAR(v_Office)||' '||v_City);
		FETCH get_offices INTO v_Office, v_City;	-- 2
		DBMS_OUTPUT.put_line('OutPutString is: '||TO_CHAR(v_Office)||' '||v_City);
		FETCH get_offices INTO v_Office, v_City;	-- 3
		DBMS_OUTPUT.put_line('OutPutString is: '||TO_CHAR(v_Office)||' '||v_City);
		FETCH get_offices INTO v_Office, v_City;	-- 4
		DBMS_OUTPUT.put_line('OutPutString is: '||TO_CHAR(v_Office)||' '||v_City);
		FETCH get_offices INTO v_Office, v_City;	-- 5
		DBMS_OUTPUT.put_line('OutPutString is: '||TO_CHAR(v_Office)||' '||v_City);

	CLOSE get_offices; 

END;
/

Получаем:

SQL> SET SERVEROUTPUT ON
SQL> 
SQL> DECLARE
  2  
  3   v_Office OFFICES.OFFICE%TYPE;
  4   v_City OFFICES.CITY%TYPE;
  5  
  6   CURSOR get_offices IS
  7       SELECT OFFICE, CITY
  8       FROM OFFICES;
  9  
 10  BEGIN
 11  
 12   OPEN get_offices;
 13  
 14    DBMS_OUTPUT.enable;
 15    -- Use operator FETCH to get variables!
 16    FETCH get_offices INTO v_Office, v_City; -- 1
 17    DBMS_OUTPUT.put_line('OutPutString is: '||TO_CHAR(v_Office)||' '||v_City);
 18    FETCH get_offices INTO v_Office, v_City; -- 2
 19    DBMS_OUTPUT.put_line('OutPutString is: '||TO_CHAR(v_Office)||' '||v_City);
 20    FETCH get_offices INTO v_Office, v_City; -- 3
 21    DBMS_OUTPUT.put_line('OutPutString is: '||TO_CHAR(v_Office)||' '||v_City);
 22    FETCH get_offices INTO v_Office, v_City; -- 4
 23    DBMS_OUTPUT.put_line('OutPutString is: '||TO_CHAR(v_Office)||' '||v_City);
 24    FETCH get_offices INTO v_Office, v_City; -- 5
 25    DBMS_OUTPUT.put_line('OutPutString is: '||TO_CHAR(v_Office)||' '||v_City);
 26  
 27   CLOSE get_offices;
 28  
 29  END;
 30  /
OutPutString is: 22 Запиндрищинск                                               
OutPutString is: 11 Красный Мотоцикл                                            
OutPutString is: 12 Чугуевск                                                    
OutPutString is: 13 Бубурино                                                    
OutPutString is: 21 Котрогайка                                                  

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

Вот все пять записей, каждый последующий FETCH вернул по одной записи! Теперь понятно? Думаю, да! И если вы уже, наверное, догадались, что здесь просто напрашивается оператор цикла! Давайте для начала (просто для примера) применим, оператор LOOP EXIT WHEN и при этом немного изменим наш курсор, применив атрибут %ROWTYPE вот так:

SET SERVEROUTPUT ON

DECLARE
	
	CURSOR get_offices IS
		SELECT * FROM OFFICES;

	v_gt get_offices%ROWTYPE;

BEGIN

	OPEN get_offices;
		
	LOOP 

		EXIT WHEN get_offices%NOTFOUND;

		DBMS_OUTPUT.enable;
		-- Use operator FETCH to get variables!
		FETCH get_offices INTO v_gt;	
		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));
	
	END LOOP;

	CLOSE get_offices; 

END;
/

И наконец получаем:

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   OPEN get_offices;
 11  
 12   LOOP
 13  
 14    EXIT WHEN get_offices%NOTFOUND;
 15  
 16    DBMS_OUTPUT.enable;
 17    -- Use operator FETCH to get variables!
 18    FETCH get_offices INTO v_gt;
 19    DBMS_OUTPUT.put_line('Get Data: '||TO_CHAR(v_gt.OFFICE)||' '||v_gt.CITY||' '
 20    ||v_gt.REGION||' '||TO_CHAR(v_gt.MGR)||' '||TO_CHAR(v_gt.TARGET)||' '||TO_CHAR(v_gt.SALES));
 21  
 22   END LOOP;
 23  
 24   CLOSE get_offices;
 25  
 26  END;
 27  /
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                                   
Get Data: 21 Котрогайка Запад 108 725 835,915                                   

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

Ура! Я вас поздравляю, мы наконец чуть забежав вперед, с применением оператора цикла и курсорного атрибута %NOTFOUND, наконец написали наш первый полноценный курсор! Который выбирает все данные из таблицы и выводит их на экран! Но, не думайте, что это все что касается курсоров! Это далеко не так! :)


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