Шаг 55 - PL/SQL - КУРСОР и его атрибуты

Настало время рассмотреть немаловажный аспект, а именно курсорные атрибуты. В PL/SQL имеется четыре основных курсорных атрибута %FOUND, %NOTFOUND, %ISOPEN и %ROWCOUNT. Атрибуты курсора объявляются подобно операторам %TYPE и %ROWTYPE, справа от имени курсора, вот так:

------------ имя курсора%атрибут -------------

Для того, чтобы понять что это за операторы и для чего они предназначены, давайте предположим, что наша учебная таблица OFFICES, содержит два столбца OFFICE и CITY, а так же имеет только две записи. Вот таким образом: (на самом деле в вашей учебной базе таблица OFFICES содержит 5ть записей и больше полей, но пока просто включите свою фантазию!) :)

  	
Таблица OFFICES

 OFFICE CITY
------- -------------------
     22 Запиндрищинск
     11 Красный Мотоцикл

Теперь к этой таблице запишем вот такой блок PL/SQL и объявим в нем курсор get_offices следующим образом:

DECLARE
	
	-- Cursor declaration --
	CURSOR get_offices IS
		SELECT * FROM OFFICES;
	-- Record to store the fetched data --
	v_gt get_offices%ROWTYPE;

BEGIN

	-- location (1) -- cursor is open --
	OPEN get_offices; 

	-- location (2)
	FETCH get_offices INTO v_gt;		-- fetch first row --
		-- location (3)
		FETCH get_offices INTO v_gt;	-- fetch second row --
			-- location (4)
			FETCH get_offices INTO v_gt;	-- Third first --

	-- location (5)
	CLOSE get_offices;	-- cursor is close --
	-- location (6)

END;
/

Теперь давайте рассмотрим по очереди все атрибуты курсоров. Начнем с атрибута %FOUND. Данный атрибут является логическим объектом и возвращает следующие значения согласно точкам, указанным в нашем курсоре. Надеюсь, что дополнительно пояснять не нужно, так как все достаточно хорошо видно в таблице!

%FOUND

ТочкаЗначение
get_offices%FOUND
Пояснение
1.Ошибка ORA-1001Курсор еще не открыт и активного набора для него не существует!
2.NULLХотя курсор открыт, не было произведено ни одного считывания строк. Значение атрибута не определено.
3.TRUEС помощью предшествующего оператора FETCH выбрана первая строк таблицы OFFICES.
4.TRUEС помощью предшествующего оператора FETCH выбрана вторая строк таблицы OFFICES.
5.FALSEПредшествующий оператор FETCH не вернул никаких данных, так как все строки активного набора выбраны.
6.Ошибка ORA-1001Курсор закрыт, и вся хранившаяся информация об активном наборе удалена.

Атрибут %NOTFOUND, как вы уже догадались, является полной противоположностью %FOUND и так же хорошо понятен из приведенной ниже таблицы:

%NOTFOUND

ТочкаЗначение
get_offices%NOTFOUND
Пояснение
1.Ошибка ORA-1001Курсор еще не открыт и активного набора для него не существует!
2.NULLХотя курсор открыт, не было произведено ни одного считывания строк. Значение атрибута не определено.
3.FALSEС помощью предшествующего оператора FETCH выбрана первая строк таблицы OFFICES.
4.FALSEС помощью предшествующего оператора FETCH выбрана вторая строк таблицы OFFICES.
5.TRUEПредшествующий оператор FETCH не вернул никаких данных, так как все строки активного набора выбраны.
6.Ошибка ORA-1001Курсор закрыт, и вся хранившаяся информация об активном наборе удалена.

Атрибут %ISOPEN так же логический объект и указывает только на то, открыт ли курсор или нет. Возвращаемые значение приведены ниже:

%ISOPEN

ТочкаЗначение
get_offices%ISOPEN
Пояснение
1.FALSEКурсор get_offices еще не открыт.
2.TRUEКурсор get_offices был открыт.
3.TRUEКурсор get_offices еще открыт.
4.TRUEКурсор get_offices еще открыт.
5.TRUEКурсор get_offices еще открыт.
6.FALSEКурсор get_offices закрыт.

Атрибут %ROWCOUNT является числовым атрибутом и возвращает число строк считанных курсором на определенный момент времени. Его значение приведены ниже:

%ROWCOUNT

ТочкаЗначение
get_offices%ROWCOUNT
Пояснение
1.Ошибка ORA-1001Курсор еще не открыт и активного набора для него не существует!
2.0Хотя курсор открыт, не было произведено ни одного считывания строк.
3.1Считана первая строка таблицы OFFICES
4.2Считана вторая строка таблицы OFFICES
5.2К данному моменту считаны две строки таблицы OFFICES
6.Ошибка ORA-1001Курсор закрыт, и вся хранившаяся информация об активном наборе удалена.

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

SET SERVEROUTPUT ON

DECLARE
	
	-- Cursor declaration --
	CURSOR get_offices IS
		SELECT * FROM OFFICES;
	-- Record to store the fetched data --
	v_gt get_offices%ROWTYPE;

BEGIN
	
	DBMS_OUTPUT.enable;
	
	IF(get_offices%ISOPEN) THEN
	DBMS_OUTPUT.put_line('Cursor get_offices is open now!');		
	ELSE
	DBMS_OUTPUT.put_line('Cursor get_offices is close now!');		
	END IF;
	
	-- location (1) -- cursor is open --
	OPEN get_offices; 

	IF(get_offices%ISOPEN) THEN
	DBMS_OUTPUT.put_line('Cursor get_offices is open now!');		
	ELSE
	DBMS_OUTPUT.put_line('Cursor get_offices is close now!');		
	END IF;
	
	-- location (2)
	FETCH get_offices INTO v_gt;	-- fetch first row --
	IF(get_offices%FOUND) THEN
	DBMS_OUTPUT.put_line('Row is Found!');
	ELSE
	DBMS_OUTPUT.put_line('Row is not Found!');
	END IF;
	DBMS_OUTPUT.put_line(TO_CHAR(v_gt.OFFICE)||' '||v_gt.CITY||' '
		||'The count row is '||TO_CHAR(get_offices%ROWCOUNT));
			
	-- location (3)
	FETCH get_offices INTO v_gt;	-- fetch second row --

	IF(get_offices%NOTFOUND) THEN
	DBMS_OUTPUT.put_line('Row is not Found!');
	ELSE
	DBMS_OUTPUT.put_line('Row is Found!');	
	END IF;
	DBMS_OUTPUT.put_line(TO_CHAR(v_gt.OFFICE)||' '||v_gt.CITY||' '
		||'The count row is '||TO_CHAR(get_offices%ROWCOUNT));

	-- location (4)
	FETCH get_offices INTO v_gt;	-- Third first --
	IF(get_offices%FOUND) THEN
	DBMS_OUTPUT.put_line('Row is Found!');
	ELSE
	DBMS_OUTPUT.put_line('Row is not Found!');
	END IF;
	DBMS_OUTPUT.put_line(TO_CHAR(v_gt.OFFICE)||' '||v_gt.CITY||' '
		||'The count row is '||TO_CHAR(get_offices%ROWCOUNT));

	-- location (5)
	CLOSE get_offices;	-- cursor is close --
	-- location (6)

END;
/

Получаем:

SQL> SET SERVEROUTPUT ON
SQL> 
SQL> DECLARE
  2 	
  3 	-- Cursor declaration --
  4 	CURSOR get_offices IS
  5 		SELECT * FROM OFFICES;
  6 	-- Record to store the fetched data --
  7 	v_gt get_offices%ROWTYPE;
  8 
  9 BEGIN
 10 	
 11 	DBMS_OUTPUT.enable;
 12 	
 13 	IF(get_offices%ISOPEN) THEN
 14 	DBMS_OUTPUT.put_line('Cursor get_offices is open now!');		
 15 	ELSE
 16 	DBMS_OUTPUT.put_line('Cursor get_offices is close now!');		
 17 	END IF;
 18 	
 19 	-- location (1) -- cursor is open --
 20 	OPEN get_offices; 
 21 
 22 	IF(get_offices%ISOPEN) THEN
 23 	DBMS_OUTPUT.put_line('Cursor get_offices is open now!');		
 24 	ELSE
 25 	DBMS_OUTPUT.put_line('Cursor get_offices is close now!');		
 26 	END IF;
 27 	
 28 	-- location (2)
 29 	FETCH get_offices INTO v_gt;	-- fetch first row --
 30 	IF(get_offices%FOUND) THEN
 31 	DBMS_OUTPUT.put_line('Row is Found!');
 32 	ELSE
 33 	DBMS_OUTPUT.put_line('Row is not Found!');
 34 	END IF;
 35 	DBMS_OUTPUT.put_line(TO_CHAR(v_gt.OFFICE)||' '||v_gt.CITY||' '
 36 		||'The count row is '||TO_CHAR(get_offices%ROWCOUNT));
 37 			
 38 	-- location (3)
 39 	FETCH get_offices INTO v_gt;	-- fetch second row --
 40 
 41 	IF(get_offices%NOTFOUND) THEN
 42 	DBMS_OUTPUT.put_line('Row is not Found!');
 43 	ELSE
 44 	DBMS_OUTPUT.put_line('Row is Found!');	
 45 	END IF;
 46 	DBMS_OUTPUT.put_line(TO_CHAR(v_gt.OFFICE)||' '||v_gt.CITY||' '
 47 		||'The count row is '||TO_CHAR(get_offices%ROWCOUNT));
 48 
 49 	-- location (4)
 50 	FETCH get_offices INTO v_gt;	-- Third first --
 51 	IF(get_offices%FOUND) THEN
 52 	DBMS_OUTPUT.put_line('Row is Found!');
 53 	ELSE
 54 	DBMS_OUTPUT.put_line('Row is not Found!');
 55 	END IF;
 56 	DBMS_OUTPUT.put_line(TO_CHAR(v_gt.OFFICE)||' '||v_gt.CITY||' '
 57 		||'The count row is '||TO_CHAR(get_offices%ROWCOUNT));
 58 
 59 	-- location (5)
 60  	CLOSE get_offices;	-- cursor is close --
 61 	-- location (6)
 62 
 63 END;
 64 /
Cursor get_offices is close now!
Cursor get_offices is open now!
Row is Found!
22 Запиндрищинск The count row is 1
Row is Found!
11 Красный Мотоцикл The count row is 2
Row is Found!
12 Чугуевск The count row is 3

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

Не пугайтесь этих 64-х строк кода, в дальнейшем наши программы будут все больше и больше, привыкайте! :) Здесь, я постарался применить все атрибуты для того, чтобы на практике показать, как они работают и устроены, по этому еще раз самостоятельно посмотрите весь код и обратите внимание на то, как срабатывают разные атрибуты при разных условиях, а на дом вам задание написать тот же курсор, применив цикл и выбрав все записи из него! Дерзайте!!! :)


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