Итак, немного разобравшись с неявными курсорами приступим вплотную к изучению явных курсоров. Если кто из вас был внимателен, и в шаге 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, но это уже отдельная тема. Пока можете поработать с этими типами и попробовать устранить мои ошибки! Удачи!