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