Курсоры (CURSOR) в PL/SQL: основные принципы программирования

Курсоры PL/SQL - принципы программированияОдной из важнейших характеристик PL/SQL является тесная интеграция с базой данных Oracle в отношении как изменения данных в таблицах, так и выборки данных из таблиц. В этом блоге рассматриваются элементы PL/SQL, связанные с выборкой информации из базы данных и ее обработкой в программах PL/SQL.

При выполнении команды SQL из PL/SQL РСУБД Oracle назначает ей приватную рабочую область, а некоторые данные записывает в системную глобальную область (SGA, System Global Area). В приватной рабочей области содержится информация о команде SQL и набор данных, возвращаемых или обрабатываемых этой командой. PL/SQL предоставляет программистам несколько механизмов доступа к этой рабочей области и содержащейся в ней информации; все они так или иначе связаны с опреде­лением курсоров и выполнением операций с ними.



 

Основные принципы работы с курсорами

Курсор проще всего представить себе как указатель на таблицу в базе данных. Напри­мер, следующее объявление связывает всю таблицу employee с курсором employee_cur:

CURSOR employee_cur IS SELECT * FROM employee;

Объявленный курсор можно открыть:

OPEN employee_cur;

Далее из него можно выбирать строки:

FETCH employee_cur INTO employee_rec;

Завершив работу с курсором, его следует закрыть:

CLOSE employee_cur;

В этом случае каждая выбранная из курсора запись представляет строку таблицы employee. Однако с курсором можно связать любую допустимую команду SELECT. В сле­дующем примере в объявлении курсора объединяются три таблицы:

DECLARE
CURSOR joke_feedback_cur IS
   SELECT J.name, R.laugh_volume, C.name FROM Joke J, response R, comedian C WHERE J.joke_id = R.joke_id AND R.joker_id = C.joker_id;
BEGIN
END;

В данном случае курсор действует не как указатель на конкретную таблицу базы дан­ных — он указывает на виртуальную таблицу или неявное представление, определяемое командой SELECT. (Такая таблица называется виртуальной, потому что команда SELECT генерирует данные с табличной структурой, но эта таблица существует только вре­менно, пока программа работает с возвращенными командой данными.) Если тройное объединение возвращает таблицу из 20 строк и 3 столбцов, то курсор действует как указатель на эти 20 строк.

 

Терминология

В PL/SQL имеется множество возможностей выполнения команд SQL, и все они реа­лизованы в программах как курсоры того или иного типа. Прежде чем приступить к их освоению, необходимо познакомиться с методами выборки данных и используемой при этом терминологией.

 

Типичные операции с запросами и курсорами

Независимо от типа курсора процесс выполнения команд SQL всегда состоит из одних и тех же действий. В одних случаях PL/SQL производит их автоматически, а в других, как, например, при использовании явного курсора, они явно организуются програм­мистом.

На рис. 1 показано, как некоторые из этих операций используются для выборки информации из базы данных в программу PL/SQL.

 

Упрощенная схема выборки данных с использованием курсора

Рис. 1. Упрощенная схема выборки данных с использованием курсора

 

 

 

Знакомство с атрибутами курсоров

В этом разделе перечисляются и вкратце описываются атрибуты курсоров.

PL/SQL поддерживает шесть атрибутов курсоров, перечисленных в табл. 1.

 

Атрибуты курсоров

Таблица 1. Атрибуты курсоров

 

 

Чтобы обратиться к атрибуту курсора, укажите в виде его префикса имя курсора или курсорной переменной и символ %:

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

В качестве имен неявных курсоров используется префикс SQL, например SQL%NOTFOUND. Ниже приведены краткие описания всех атрибутов курсоров.

 

Атрибут %FOUND

Атрибут %FOUND возвращает информацию о состоянии последней операции FETCH с курсо­ром. Если последний вызов FETCH для курсора вернул строку, то возвращается значение TRUE, а если строка не была получена, возвращается FALSE.

Если курсор еще не был открыт, база данных инициирует исключение INVALID_CURS0R. В следующем примере я перебираю все строки курсора caller_cur, присваиваю все звонки, введенные до сегодняшнего дня для конкретного позвонившего, после чего пере­хожу к следующей записи. При достижении последней записи атрибут %FOUND явного курсора возвращает FALSE, и выполнение простого цикла прерывается. Атрибут %FOUND неявного курсора также проверяется после команды UPDATE:


FOR caller_rec IN caller_cur LOOP
   UPDATE call
   SET caller_id = caller_rec.caller_id WHERE call_timestamp < SYSDATE;
   IF SQL%FOUND THEN
      DBMS_OUTPUT.PUT_LINE ('Calls updated for ' || caller_rec.caller_id);
   END IF;
END LOOP;

 

 

Атрибут %NOTFOUND

Атрибут %NOTFOUND по смыслу противоположен %FOUND: он возвращает TRUE, если по­следняя операция FETCH с курсором не вернула строки (как правило, из-за того, что последняя строка уже была прочитана). Если курсор не может вернуть строку из-за ошибки, инициируется соответствующее исключение.

Если курсор еще не был открыт, база данных инициирует исключение INVALID_CURSOR. В каких случаях следует использовать %FOUND, а когда предпочтение отдается %NOTFOUND? Используйте ту формулировку, которая более естественно подходит для вашего кода. В предыдущем примере для выхода из цикла использовалась следующая команда:

EXIT WHEN NOT caller_cur%FOUND;

Альтернативная — и возможно, более понятная — формулировка могла бы использовать

%NOTFOUND:
EXIT WHEN caller_cur%NOTFOUND;

 

Атрибут %ROWCOUNT

Атрибут %ROWCOUNT возвращает количество записей, прочитанных из курсора к моменту запроса атрибута. При исходном открытии курсора атрибут %ROWCOUNT равен 0. При обра­щении к атрибуту %ROWCOUNT курсора, который еще не был открыт, инициируется исклю­чение INVALID_CURSOR. После выборки каждой записи %ROWCOUNT увеличивается на единицу. Используйте атрибут %ROWCOUNT для проверки того, что программа прочитала (или обновила в случае DML) нужное количество строк, или для прерывания выполнения после заданного числа итераций. Пример:

BEGIN
UPDATE employees SET last_name = 'FEUERSTEIN';
DBMS_OUTPUT.PUT_LINE (SQL%ROWCOUNT);
END;

 

 

Атрибут %ISOPEN

Атрибут %ISOPEN возвращает TRUE, если курсор открыт; в противном случае возвраща­ется FALSE.

Приведем типичный пример использования этого атрибута, который гарантирует, что курсор не остается открытым, если в программе произойдет что-то непредвиденное:

DECLARE
CURSOR happiness_cur IS SELECT simple_delights FROM ...;
BEGIN
   OPEN happiness_cur;
   IF happiness_cur%ISOPEN THEN ...
      EXCEPTION
      WHEN OTHERS THEN
      IF happiness_cur%ISOPEN THEN close happiness_cur;
   END IF;
END;

 

 

Атрибут %BULK_ROWCOUNT

Атрибут %BULK_ROWCOUNT, предназначенный для использования с командой FORALL, воз­вращает количество строк, обработанных при каждом выполнении DML. Этот атрибут обладает семантикой ассоциативного массива.

 

 

Атрибут %BULK_EXCEPTIONS

Атрибут %BULK_EXCEPTIONS, также предназначенный для использования с командой FORALL, возвращает информацию об исключении, которое может быть инициировано при каждом выполнении DML. Этот атрибут обладает семантикой ассо­циативного массива записей.

На атрибуты курсоров можно ссылаться в коде PL/SQL, как показано в приве­денных примерах, но вы не сможете напрямую обращаться к ним в команде SQL. Например, при попытке использовать атрибут %ROWCOUNT в секции WHERE команды SELECT:


SELECT caller_id, company_id FROM caller 
WHERE company_id = company_cur%ROWCOUNT;

компилятор выдает ошибку PLS-00229: Attribute expression within SQL expression.

 

Ссылки на переменные PL/SQL в курсорах

Поскольку курсор должен быть связан с командой SELECT, в нем всегда присутствует хотя бы одна ссылка на таблицу базы данных; по ней (и по содержимому условия WHERE) Oracle определяет, какие строки будут возвращены в составе активного набора. Однако это не означает, что команда SELECT может возвращать информацию только из базы данных.

Список выражений, задаваемых между ключевыми словами SELECT и FROM, называется списком выборки. Во встроенном SQL список выборки может содержать столбцы и вы­ражения (вызовы SQL-функций для этих столбцов, константы и т. д.). В PL/SQL список выборки обычно содержит переменные PL/SQL и сложные выражения.

 На локальные данные программы PL/SQL (переменные и константы), а также на перемен­ные привязки хост-среды можно ссылаться в предложениях WHERE, GROUP BY и HAVING команды SELECT курсора. Ссылки на переменные PL/SQL можно (и должно) уточнять именем ее области видимости (именем процедуры, именем пакета и т. д.), особенно в командах SQL.

 

Выбор между явным и неявным курсорами

Все последние годы знатоки Oracle (включая и авторов данной книги) убежденно до­казывали, что для однострочной выборки данных никогда не следует использовать неявные курсоры. Это мотивировалось тем, что неявные курсоры, соответствуя стан­дарту ISO, всегда выполняют две выборки, из-за чего они уступают по эффективности явным курсорам.

Так утверждалось и в первых двух изданиях этой книги, но пришло время нарушить эту традицию (вместе со многими другими). Начиная с Oracle8, в результате целенаправ­ленных оптимизаций неявные курсоры выполняются даже эффективнее эквивалентных явных курсоров.

Означает ли это, что теперь всегда лучше пользоваться неявными курсорами? Вовсе нет. В пользу применения явных курсоров существуют убедительные доводы.

Поэтому вместо формулировки «явный или неявный?» лучше спросить: «инкапсули­рованный или открытый?» И ответ будет таким: всегда инкапсулируйте однострочные запросы, скрывая их за интерфейсом функции (желательно пакетной) и возвращая данные через RETURN.

Не жалейте времени на инкапсуляцию запросов в функциях, желательно пакетных. Это позволит вам и всем остальным разработчикам вашей группы просто вызвать функцию, когда появится необходимость в данных. Если Oracle изменит правила обработки запро­сов, а ваши предыдущие наработки станут бесполезными, достаточно будет изменить реализацию всего одной функции.

 

Вас заинтересует / Intresting for you:

Управление приложениями PL/SQL...
Управление приложениями PL/SQL... 4651 просмотров Stas Belkov Thu, 16 Jul 2020, 06:20:48
Встроенные методы коллекций PL...
Встроенные методы коллекций PL... 14849 просмотров sepia Tue, 29 Oct 2019, 09:54:01
Курсорные выражения (CURSOR) в...
Курсорные выражения (CURSOR) в... 8330 просмотров Максим Николенко Wed, 29 Aug 2018, 16:05:38
Символьные функции и аргументы...
Символьные функции и аргументы... 18590 просмотров Анатолий Wed, 23 May 2018, 18:54:01
Печать
Войдите чтобы комментировать

apv аватар
apv ответил в теме #9246 5 года 6 мес. назад
Атрибут %FOUND искал описание. Здесь разъяснили хорошо в блоге. Благодарю!
OraCool аватар
OraCool ответил в теме #9207 5 года 7 мес. назад
Понравилось описание атрибутов курсоров. Спасибо за публикацию!