Курсор это средство извлечения данных из БД. Курсоры содержат определения столбцов и объектов. Курсоры используются для получения строк, возвращаемых запросом. Для передачи строк в курсор используется запрос, после чего вы можете выбирать строки из курсора по одной за один раз.
create table t1(id, type, text) as select object_id, object_type, object_name from all_objects;
create table t1 as select object_id id, object_type type, object_name text from all_objects;
select id, type, text from t1 where /> 17367 SCHEDULE FILE_WATCHER_SCHEDULE
select id, type, text from t1 where type = ‘SCHEDULE’;
BEGIN SELECT text INTO v_text FROM t1 WHERE /> DBMS_OUTPUT.PUT_LINE( ‘text = ‘ || v_text ); END; /
В ходе выполнения кода создается курсор для выборки значения text.
Явный курсор определяется до начала выполнения:
DECLARE CURSOR c_get_text IS SELECT text FROM t1 WHERE /> v_text t1.text%TYPE;
BEGIN OPEN c_get_text; FETCH c_get_text INTO v_text; DBMS_OUTPUT.PUT_LINE( ‘text = ‘ || v_text ); CLOSE c_get_text; END; /
Преимущество явного курсора заключается в наличии у него атрибутов, облегчающих применение условных операторов.
CREATE OR REPLACE PROCEDURE proc1 AS CURSOR c_get_text IS SELECT text FROM t1 WHERE /> v_text t1.text%TYPE;
BEGIN OPEN c_get_text; FETCH c_get_text INTO v_text; IF c_get_text%NOTFOUND THEN DBMS_OUTPUT.PUT_LINE( ‘Данные не найдены. ‘ ); ELSE DBMS_OUTPUT.PUT_LINE( ‘text = ‘ || v_text ); END IF; CLOSE c_get_text; END; /
А как подобное сделать с неявным курсором:
CREATE OR REPLACE PROCEDURE proc2 AS v_text t1.text%TYPE; v_bool BOOLEAN := TRUE;
BEGIN BEGIN SELECT text INTO v_text FROM t1 WHERE /> EXCEPTION WHEN no_data_found THEN v_bool := FALSE; WHEN others THEN RAISE; END;
IF NOT v_bool THEN DBMS_OUTPUT.PUT_LINE( ‘Данные не найдены. ‘ ); ELSE DBMS_OUTPUT.PUT_LINE( ‘text = ‘ || v_text ); END IF; END; /
Пришлось заниматься перехватом исключений, чтобы определить, была ли найдена запись.
Параметризация курсоров помогает повысить степень их повторного использования.
курсор с параметром:
DECLARE CURSOR c_get_text(par1 NUMBER) IS SELECT text FROM t1 WHERE /> v_text t1.text%TYPE;
BEGIN OPEN c_get_text(17367); FETCH c_get_text INTO v_text; DBMS_OUTPUT.PUT_LINE( ‘text = ‘ || v_text ); CLOSE c_get_text; END; /
Переменные типа REF CURSOR могут ссылаться на любые реальные курсоры. Программа, использующая тип REF CURSOR, может работать с курсорами, не заботясь о том, какие конкретно данные будут извлечены ими во время выполнения.
CREATE OR REPLACE PROCEDURE proc_ref AS v_curs SYS_REFCURSOR; v_text t1.text%TYPE;
BEGIN OPEN v_curs FOR ‘SELECT text ‘ || ‘FROM t1 ‘ || ‘WHERE /> FETCH v_curs INTO v_text;
DBMS_OUTPUT.PUT_LINE( ‘text = ‘ || v_text );
Например я могу создать функцию, которая принимает некий входной параметр, создает курсор и возвращает тип REF CURSOR :
CREATE OR REPLACE FUNCTION func1(par1 NUMBER) RETURN SYS_REFCURSOR IS v_curs SYS_REFCURSOR;
BEGIN OPEN v_curs FOR ‘SELECT text ‘ || ‘FROM t1 ‘ || ‘WHERE /> || par1;
Другой пользователь может воспользоваться этой функцией так:
v_curs SYS_REFCURSOR; v_text t1.text%TYPE;
BEGIN v_curs := func1(17367);
FETCH v_curs INTO v_text;
IF v_curs%NOTFOUND THEN DBMS_OUTPUT.PUT_LINE( ‘Данные не найдены. ‘ ); ELSE DBMS_OUTPUT.PUT_LINE( ‘text = ‘ || v_text ); END IF;
Для пользователя, вызывающего функцию func1(), она для него представляет черный ящик, возвращающий курсор.
Сильнотипизированный и слаботипизированный REF CURSOR.
TYPE имя_типа_курсора IS REF CURSOR [ RETURN возвращаемый_тип ];
TYPE refcursor IS REF CURSOR RETURN table1%ROWTYPE;
TYPE refcursor IS REF CURSOR;
Первая форма REF CURSOR называется сильно типизированной, поскольку тип структуры, возвращаемый курсорной переменной, задается в момент объявления (непосредственно или путем привязки к типу строки таблицы).
Вторая форма (без предложения RETURN) называется слаботипизированной. Тип возвращаемой структуры данных для нее не задается. Такая курсорная переменная обладает большей гибкостью, поскольку для нее можно задавать любые запросы с любой структурой возвращаемых данных.
В Oracle 9i появился предопределенный слабый тип REF CURSOR с именем SYS_REFCURSOR, теперь можно не определять собственный слабый тип, достаточно использовать стандартный тип Oracle:
DECLARE my_cursor SYS_REFCURSOR;
Пример сильнотипизированного курсора:
TYPE my_type_rec IS RECORD (text t1.text%TYPE); TYPE my_type_cur IS REF CURSOR RETURN my_type_rec; v_curs my_type_cur; v_text t1.text%TYPE;
BEGIN OPEN v_curs FOR SELECT text FROM t1 WHERE /> FETCH v_curs INTO v_text;
DBMS_OUTPUT.PUT_LINE( ‘text = ‘ || v_text );
TYPE my_type_cur IS REF CURSOR RETURN t1%ROWTYPE; v_curs my_type_cur; v_var t1%ROWTYPE;
BEGIN OPEN v_curs FOR SELECT * FROM t1 WHERE /> FETCH v_curs INTO v_var;
id = 17367, type = SCHEDULE, text = FILE_WATCHER_SCHEDULE
Пример слаботипизированного курсора:
TYPE my_type_cur IS REF CURSOR; v_curs my_type_cur; v_text t1.text%TYPE;
BEGIN OPEN v_curs FOR SELECT text FROM t1 WHERE /> FETCH v_curs INTO v_text;
DBMS_OUTPUT.PUT_LINE( ‘text = ‘ || v_text );
v_curs SYS_REFCURSOR; v_text t1.text%TYPE;
BEGIN OPEN v_curs FOR SELECT text FROM t1 WHERE /> FETCH v_curs INTO v_text;
DBMS_OUTPUT.PUT_LINE( ‘text = ‘ || v_text );
Курсор можно передавать в качестве параметра:
1. Функция принимающая курсор
CREATE OR REPLACE FUNCTION get_cursor(p_curs SYS_REFCURSOR) RETURN VARCHAR2 IS v_text t1.text%TYPE;
FETCH p_curs INTO v_text;
IF p_curs%NOTFOUND THEN DBMS_OUTPUT.PUT_LINE( ‘Данные не найдены. ‘ ); ELSE DBMS_OUTPUT.PUT_LINE( ‘Данные найдены. ‘ ); END IF;
2. Процедура принимающая текст SQL
CREATE OR REPLACE PROCEDURE get_sql (p_sql VARCHAR2) IS v_curs SYS_REFCURSOR; v_res VARCHAR2(50); BEGIN IF v_curs%ISOPEN THEN CLOSE v_curs; END IF; BEGIN OPEN v_curs FOR p_sql; EXCEPTION WHEN OTHERS THEN RAISE_APPLICATION_ERROR(-20000, ‘Unable to open cursor’); END; v_res := get_cursor(v_curs); CLOSE v_curs; DBMS_OUTPUT.PUT_LINE(v_res); END; /
BEGIN get_sql( ‘SELECT text FROM t1 WHERE ); END; /
Представьте себе вариант решения задачи, когда вам нужно распечатать список сотрудников, но вы не знаете какой должен быть фильтр (условие WHERE). Реализация этой логики выглядит следующим бразом: 1. Курсор по таблице EMP. 2. Печать всей информации с помощью процедуры DBMS_OUTPUT.PUT_LINE.
В данном подходе есть значительный камень преткновения. Как вы можете построить курсор, если не знаете, содержимое фильтра данных? (То есть, что делать, если вы не знаете, какие столбцы и условия необходимы в блоках WHERE и SELECT запроса?) Динамический SQL позволяет вам ответить на этот вопрос и в сочетании с Oracle построить так называемый REF CURSOR.
Вы можете использовать этот подход для создания указателей на целые наборы данных, но определив Oracle-курсор, необходимо задать запрос для его использования. Конструкции же типа REF CURSOR просто логические указатели, которые не требуют определять используемый в курсоре запрос в момент декларации.
Создание типа данных REF CURSOR
Для использования REF CURSOR переменных, необходимо использовать тип данных REF CURSOR. Тип данных REF CURSOR не может быть использован вне PL/SQL среды. Есть два типа REF CURSOR-ов: слабые и сильные (weak and strong). Слабый REF CURSOR может указывать на любой набор данных, как показано здесь:
Для небольших проектов с ограниченными возможностями, создавая свой собственный пользовательский тип может быть жизнеспособной стратегией. Тем не менее, в сложных ситуациях или нескольких окружающей среды, это лучшая идея использовать встроенные в стандартные слабого типа данных REF CURSOR чтобы избежать путаницы между разработчиками, как показано здесь:
Сильный REF CURSOR явно заявляет тип данных, на которые можно ссылаться. В следующем примере, только запросы, которые возвращают строки так же, как в таблице EMP допускаются:
Определение запроса с OPEN…FOR
Работа с переменными типа REF CURSOR аналогична работе с явными курсорами. (Переменные курсоров также применимы к REF CURSOR-ам.) Вы можете определить запрос для реф-курсора напрямую в конструкции OPEN…FOR вместо того чтобы опредлять его при декларации курсора:
Также возможно открыть рефкурсор для SELECT-запроса заключенного в строку (в данном случае используется слабый REF CURSOR):
Использование слабого реф-курсора:
Примечания: Строка 4: Создание переменной курсора. Как отметили ранее, лучше использовать предопределенных типов данных SYS_REFCURSOR для слабых REF-курсоров вместо определения одного из ваших собственных. Строка 7-8: Открытие переменной курсора для всей строки SELECT-запроса, построенного на лету.
Объявление типа возвращаемых данных
Как вы заметили в предыдущем примере отсутствует часть кода (. ) имена переменных в которых происходит выборка строки данных. Так SELECT-запрос строится динамически, у Oracle нет возможности проверить количество и тип полей которые планируется вернуть. Т.к. мы планируем вернуть все поля, рассмотрим три варианта как это возможно сделать:
Пример использования типа данных EMP%ROWTYPE
Примечания: Строка 5: Создание новой переменной v_emp_rec. Строка 11: Выборка данных из курсора в новую переменную. Строка 13: Вывод на экран всей информации о сотруднике. Используем следующий код для проверки функциональности:
В результате мы видим всю информацию о сотрудниках из отдела №10.
Запись опубликована 18.11.2010 в 2:55 дп и размещена в рубрике Динамический SQL и PL/SQL. Вы можете следить за обсуждением этой записи с помощью ленты RSS 2.0. Можно оставить комментарий или сделать обратную ссылку с вашего сайта.
All public static methods are thread-safe, although instance methods do not guarantee thread safety.
To minimize the number of open server cursors, OracleRefReader objects should be explicitly disposed.
OracleRefCursor Members
OracleRefCursor members are listed in the following tables.
OracleRefCursor Static Methods
OracleRefCursor static methods are listed in Table 13-28.
Table 13-28 OracleRefCursor Static Methods
Methods
Description
Inherited from System.Object (Overloaded)
OracleRefCursor Static Fields
OracleRefCursor static field is listed in Table 13-29.
Table 13-29 OracleRefCursor Static Field
Methods
Description
Represents a null value that can be assigned to an OracleRefCursor instance
OracleRefCursor properties are listed in Table 13-30.
Table 13-30 OracleRefCursor Properties
Properties
Description
A reference to the OracleConnection used to fetch the REF CURSOR data
Specifies the size that the OracleDataReader internal cache needs to store result set data
Indicates whether or not the OracleRefCursor is null
Specifies the amount of memory the OracleRefcursor internal cache needs to store one row of data
OracleRefCursor Instance Methods
OracleRefCursor instance methods are listed in Table 13-31.
Table 13-31 OracleRefCursor Instance Methods
Methods
Description
Disposes the resources allocated by the OracleRefCursor object
Inherited from System.Object (Overloaded)
Returns an OracleDataReader object for the REF CURSOR
Inherited from System.Object
Inherited from System.Object
Inherited from System.Object
OracleRefCursor Static Methods
OracleRefCursor static methods are listed in Table 13-32.
Table 13-32 OracleRefCursor Static Methods
Methods
Description
Inherited from System.Object (Overloaded)
OracleRefCursor Static Fields
OracleRefCursor static field is listed in Table 13-32.
Table 13-33 OracleRefCursor Static Field
Methods
Description
Represents a null value that can be assigned to an OracleRefCursor instance
This static field represents a null value that can be assigned to an OracleRefCursor instance.
OracleRefCursor Properties
OracleRefCursor properties are listed in Table 13-34.
Table 13-34 OracleRefCursor Properties
Properties
Description
A reference to the OracleConnection used to fetch the REF CURSOR data
Specifies the size that the OracleDataReader internal cache needs to store result set data
Indicates whether or not the OracleRefCursor is null
Specifies the amount of memory the OracleRefcursor internal cache needs to store one row of data
Connection
This property refers to the OracleConnection used to fetch the REF CURSOR data.
FetchSize
This property specifies the size that the OracleDataReader internal cache needs to store result set data.
A long that specifies the size (in bytes) of the OracleRefCursor internal cache.
The FetchSize property value is inherited by the OracleCommand that created the OracleRefCursor object. The FetchSize property on the OracleDataReader object determines the amount of data the OracleRefCursor fetches into its internal cache for each database round-trip.
The RowSize and FetchSize properties handle UDT and XMLType data differently than other scalar data types. Because only a reference to the UDT and XMLType data is stored in the ODP.NET’s internal cache, the RowSize property accounts for only the memory needed for the reference (which is very small) and not the actual size of the UDT and XMLType data. Thus, applications can inadvertently fetch a large number of UDT or XMLType instances from the database in a single database round-trip. This is because the actual size of UDT and XMLType data does not count against the FetchSize, and it would require numerous UDT and XMLType references to fill up the default cache size of 131072 bytes. Therefore, when fetching UDT or XMLType data, the FetchSize property must be appropriately configured to control the number of UDT and XMLType instances that are to be fetched, rather than the amount of the actual UDT and XMLType data to be fetched.
NOTE: For LOB and LONG data types, only the sizes specified in the InitialLOBFetchSize and InitialLONGFetchSize properties are accounted for by the RowSize property in addition to the metadata and reference information that is maintained by the cache for each LOB in the select list.
Курсорные переменные и REF CURSOR в PL/SQL на примерах
Курсорная переменная ссылается на курсор. В отличие от явного курсора, имя которого в PL/SQL используется как идентификатор рабочей области результирующего набора строк, курсорная переменная содержит ссылку на эту рабочую область. Явные и неявные курсоры имеют статическую природу, поскольку они жестко привязаны к конкретным запросам. С помощью же курсорной переменной можно выполнить любой запрос и даже несколько разных запросов в одной программе.
Важнейшим преимуществом курсорных переменных является то, что они предоставляют механизм передачи результатов запроса (выбранных из строк курсора) между разными программами PL/SQL, в том числе между клиентскими и серверными программами. До выхода PL/SQL Release 2.3 приходилось выбирать из курсора все данные, сохранять их в переменных PL/SQL (например, в коллекции) и передавать эти данные в аргументах. А курсорная переменная позволяет передать другой программе ссылку на объект курсора, чтобы та могла работать с его данными. Это упрощает программный код и повышает его эффективность.
Кроме того, курсоры могут совместно использоваться несколькими программами. Например, в архитектуре «клиент-сервер» клиентская программа может открыть курсор и начать выборку из него данных, а затем передать указывающую на него переменную в качестве аргумента хранимой процедуре на сервере. Эта процедура продолжит выборку, а некоторое время спустя снова передаст управление клиентской программе, которая закроет курсор. Так же могут взаимодействовать и две разные хранимые программы из одного или разных экземпляров Oracle.
Описанный процесс схематически показан на рис. 1. Он демонстрирует интересные новые возможности программ PL/SQL — совместное использование данных и управление курсорами.
Рис. 1. Передача ссылки на курсор между программами
Когда используются курсорные переменные?
Ниже перечислены основные области применения курсорных переменных.
Сходство со статическими курсорами
При проектировании курсорных переменных одно из важнейших требований заключалось в том, что семантика управления объектами курсоров по возможности должна совпадать с семантикой управления статическими курсорами. Хотя объявления курсорных переменных и синтаксис их открытия были усовершенствованы, следующие операции с курсорными переменными не отличаются от операций со статическими курсорами:
Объявление типов REF CURSOR
По аналогии с таблицами PL/SQL или записями типа, определяемого программистом, курсорная переменная объявляется в два этапа.
Синтаксис объявления типа курсора:
Обратите внимание, что секция RETURN в объявлениях типа REF CURSOR не обязательна, поэтому допустимы оба следующих объявления:
Вторая форма (без предложения RETURN ) называется слаботипизированной. Тип возвращаемой структуры данных для нее не задается. Курсорная переменная, объявленная на основе такого типа, обладает большей гибкостью, поскольку для нее можно задавать любые запросы с любой структурой возвращаемых данных, причем с помощью одной и той же переменной можно поочередно выполнить несколько разных запросов с результатами разных типов.
Объявление курсорной переменной
Синтаксис объявления курсорной переменной таков:
Пример создания курсорной переменной:
Рис. 2. Курсоры и курсорные переменные
Важно понимать различие между операцией объявления курсорной переменной и операцией создания реального объекта курсора, то есть результирующего набора строк, определяемого командой SQL курсора. Как известно, константа является значением, а переменная — указателем на значение; аналогичным образом статический набор является набором данных, а курсорная переменная — указателем на этот набор. Различие продемонстрировано на рис. 2. Обратите внимание на то, что две разные переменные в разных программах ссылаются на один и тот же объект курсора.
Открытие курсорной переменной
Значение (объект курсора) присваивается курсорной переменной при открытии курсора. Таким образом, синтаксис традиционной команды OPEN позволяет использовать после секции FOR команду SELECT :
Рис. 3. Совместимость типа данных REF CURSOR и типа данных значений, возвращаемых командой SELECT
Если курсорной переменной еще не присвоен никакой объект курсора, команда OPEN FOR неявно создает его. Если же переменная уже указывает на объект курсора, команда OPEN FOR не создает новый курсор, а ассоциирует с существующим новый запрос. Таким образом, объект курсора является структурой, отдельной и от курсорной переменной, и от запроса.
Выборка данных из курсорной переменной
Как уже упоминалось, синтаксис команды FETCH для курсорной переменной не отличается от синтаксиса статического курсора:
Если курсорная переменная объявляется в сильнотипизированной форме, компилятор PL/SQL проверяет совместимость типов структур в предложении INTO со структурой запроса, связанного с курсорной переменной.
Если курсорная переменная объявляется в слаботипизированной форме, компилятор не сможет выполнить подобную проверку. Данные из такой курсорной переменной могут извлекаться в любые структуры данных, поскольку компилятор не знает, какой объект курсора (и какая команда SQL) будет ей присвоен на момент выборки.
Обработка исключения ROWTYPE_MISMATCH
В следующем примере в таблицах централизованной базы данных хранится информация о разных видах недвижимости: одна таблица для домов, другая для коммерческих строений и т. д. Также существует одна центральная таблица с адресами и типами зданий (жилой дом, коммерческое сооружение и т. д.). Я использую одну процедуру для открытия слабой переменной REF CURSOR для таблицы, соответствующей адресу объекта. Затем каждое бюро недвижимости вызывает эту процедуру для перебора соответствующих строений. Общая последовательность действий выглядит так:
2. Созданная процедура используется для перебора строений.
В следующем примере я передаю адрес, а затем пытаюсь выполнить выборку из курсора в предположении, что адрес относится к жилому дому. Если адрес в действительности относится к коммерческой недвижимости, PL/SQL инициирует исключение ROWTYPE_MISMATCH из-за несовместимости структур записей. Затем раздел исключений снова осуществляет выборку, на этот раз в запись коммерческого строения, и перебор завершается:
Правила использования курсорных переменных
В этом разделе более подробно рассматриваются правила и вопросы, связанные с использованием курсорных переменных в программах. Речь пойдет о правилах соответствия типов данных строк, псевдонимах курсорных переменных и области их действия. Прежде всего помните, что курсорная переменная — это ссылка на объект курсора, представляющий данные, выбранные из базы данных с помощью содержащегося в нем запроса. Это не сам объект курсора. Курсорная переменная может быть связана с определенным запросом при выполнении одного из следующих условий:
С курсорными переменными может использоваться операция присваивания, и эти переменные могут передаваться в качестве аргументов процедурам и функциям. Чтобы иметь возможность выполнять такие операции, переменные должны удовлетворять правилам соответствия типов строк, проверяемым во время компиляции или во время выполнения, в зависимости от типа курсорных переменных.
Правила соответствия типов строк, проверяемые во время компиляции
В процессе компиляции программы PL/SQL проверяет соблюдение следующих правил:
Правила соответствия типов строк, проверяемые во время выполнения
При выполнении программы PL/SQL проверяет, соблюдаются ли следующие правила:
Псевдоним объекта курсора
Если одна курсорная переменная присваивается другой курсорной переменной, то обе они становятся псевдонимами одного и того же объекта курсора. В данном случае в результате присваивания в принимающую переменную копируется только ссылка на объект курсора. Результаты любой операции с этим курсором, выполняемые через одну из переменных, сразу же становятся доступными для другой переменной.
Следующий анонимный блок демонстрирует принцип действия псевдонимов курсоров:
Основные действия, выполняемые этим кодом, описаны в следующей таблице.
Любое изменения состояния объекта курсора отражается во всех ссылающихся на него переменных.
Область действия объекта курсора
В следующем примере я использую вложенные блоки для демонстрации того, как объект курсора продолжает существовать вне области действия, в которой он был изначально создан:
Передача курсорных переменных в аргументах
идентификация типа REF CURSOR
В заголовке программы необходимо идентифицировать тип параметра курсорной переменной. Для этого он должен быть заранее объявлен.
Если вы создаете локальный модуль внутри другой программы, тип курсора можно определить в той же программе. Пример:
Если вы создаете отдельную процедуру или функцию, сослаться на существующий тип REF CURSOR можно только одним способом: разместив команду TYPE в пакете. Все переменные, объявленные в спецификации пакета, становятся глобальными в рамках сеанса, и для ссылок на этот тип курсора может использоваться точечный синтаксис. Последовательность действий в этом случае должна быть такой:
Назначение режима параметра
Курсорные переменные, как и любые параметры, могут работать в одном из трех режимов:
Помните, что значение курсорной переменной представляет собой ссылку на объект курсора, а не состояние этого объекта. Иначе говоря, значение курсорной переменной не изменяется после выборки данных или закрытия курсора.
Значение курсорной переменной (то есть объект курсора, на который указывает переменная) может измениться только в результате выполнения двух операций:
Если курсорная переменная уже указывает на объект курсора, OPEN FOR не изменяет ссылку; изменяется только запрос, связанный с объектом.
Операции FETCH и CLOSE изменяют состояние объекта курсора, но не ссылку на объект, которая является значением курсорной переменной.
Пример программы, использующей курсорные переменные в параметрах:
Процедура копирует старую курсорную переменную в новую переменную. Первый параметр объявлен с режимом IN, потому что он используется только в правой части присваивания. Второй параметр должен быть объявлен в режиме OUT (или IN OUT), потому что его значение изменяется внутри процедуры. Обратите внимание: тип curvar_type определяется в пакете company.
Ограничения на использование курсорных переменных
Использование курсорных переменных подчиняется следующим ограничениям (возможно, компания Oracle снимет некоторые из них в будущих версиях):