Вместо выполнения всего запроса целиком можно настроить курсор объект, который инкапсулирует запрос, а затем считывать результат запроса по несколько строк за раз. Одной из причин для этого является предотвращение переполнения памяти в случаях, когда результат содержит большое количество строк. (Однако PL/pgSQL пользователям обычно не требуется
беспокоиться об этом, так как FOR циклы автоматически используют курсор на внутреннем уровне, чтобы избежать проблем с памятью.) Более интересным вариантом использования является возврат ссылки на курсор, созданный функцией, что позволяет вызывающей стороне считывать строки. Это обеспечивает эффективный способ возврата больших наборов строк из функций.
Весь доступ к курсорам в PL/pgSQL осуществляется через
переменные курсора, которые всегда имеют специальный тип данных
refcursor. Один из способов создания переменной курсора — просто объявить её как переменную типа refcursor.
Другой способ заключается в использовании синтаксиса объявления курсора,
который в общем виде выглядит следующим образом:
имя[ [ NO ] SCROLL ] CURSOR [ (аргументы) ] FORзапрос;
(FOR можно заменить на IS для
Oracle совместимости.)
Если SCROLL указано, курсор будет поддерживать прокрутку в обратном направлении; если NO SCROLL указано, операции выборки
в обратном направлении будут отклонены; если ни одна из спецификаций не указана, возможность выборки в обратном направлении будет зависеть от особенностей запроса.
аргументы, если этот параметр указан, он представляет собой разделенный запятыми список пар , которые определяют имена, подлежащие замене значениями параметров в данном запросе. Фактические
значения для замены этих имен будут указаны позже,
при открытии курсора.
имя
тип данных
Некоторые примеры:
DECLARE
curs1 refcursor;
curs2 CURSOR FOR SELECT * FROM tenk1;
curs3 CURSOR (key integer) FOR SELECT * FROM tenk1 WHERE unique1 = key;
Все три переменные имеют тип данных refcursor, но первую можно использовать с любым запросом, тогда как со второй уже связан полностью определенный запрос связанный , а с последней
связан параметризованный запрос. (key будет
заменен целочисленным значением параметра при открытии курсора.)
Переменная curs1
считается несвязанной , так как она не связана ни с каким конкретным запросом.
Предложение SCROLL параметр нельзя использовать, когда в запросе курсора применяется FOR UPDATE/SHARE. Кроме того, рекомендуется использовать NO SCROLL с запросом, в котором задействованы изменяемые функции (volatile). Реализация SCROLL
предполагает, что повторное чтение выходных данных запроса даст согласованные результаты, чего изменчивая функция может не обеспечить.
Прежде чем курсор можно будет использовать для получения строк, его необходимо
открыть. (Это действие эквивалентно команде SQL DECLARE
CURSOR.)
PL/pgSQL предусматривает
три формы оператора OPEN , две из которых используют несвязанные переменные курсора, тогда как в третьей используется связанная переменная курсора.
Связанные переменные курсора также можно использовать без явного открытия курсора с помощью FOR оператора, описанного в
Раздел 5.6.7.4. FOR цикл откроет курсор, а затем снова закроет его после завершения цикла.
Открытие курсора подразумевает создание внутренней структуры данных сервера, называемой портал, которая содержит состояние выполнения запроса курсора. Портал имеет имя, которое должно быть уникальным в рамках сессии на протяжении всего времени существования портала. По умолчанию, PL/pgSQL будет присваивать уникальное имя каждому создаваемому порталу. Однако, если переменной курсора присвоено непустое строковое значение, эта строка будет использоваться в качестве имени портала. Данная функциональность может использоваться так, как описано в Раздел 5.6.7.3.5.
OPEN FOR запрос #OPENunbound_cursorvar[ [ NO ] SCROLL ] FORзапрос;
Переменная курсора открывается, и ей передается указанный запрос для
выполнения. Курсор не должен быть уже открыт, и он должен быть
объявлен как несвязанная переменная курсора (то есть как простая
refcursor переменная). Запрос должен представлять собой команду
SELECT, или иную конструкцию, возвращающую строки
(например, EXPLAIN). Запрос
обрабатывается так же, как и другие команды SQL в
PL/pgSQL: PL/pgSQL
имена переменных подставляются, а план запроса кэшируется для
возможного повторного использования. Когда PL/pgSQL
переменная подставляется в запрос курсора, используется то значение,
которое она имела в момент открытия; OPEN;
последующие изменения переменной не повлияют на работу
курсора.
SCROLL и NO SCROLL
параметры имеют те же значения, что и для связанного курсора.
Пример:
OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey;
OPEN FOR EXECUTE #OPENunbound_cursorvar[ [ NO ] SCROLL ] FOR EXECUTEquery_string[ USINGвыражение[, ... ] ];
Переменная курсора открывается, и ей передается указанный запрос для
выполнения. Курсор не должен быть уже открыт, и он должен быть
объявлен как несвязанная переменная курсора (то есть как простая
refcursor переменной). Запрос задается в виде строкового
выражения так же, как и в EXECUTE
команде. Как обычно, это обеспечивает гибкость, позволяя плану запроса меняться
от одного выполнения к другому (см. Раздел 5.6.11.2),
это также означает, что подстановка переменных не выполняется в
строке команды. Как и в случае с EXECUTE, значения параметров
можно вставить в динамическую команду с помощью
format() и USING.
SCROLL и
NO SCROLL параметры имеют те же значения, что и для связанного
курсора.
Пример:
OPEN curs1 FOR EXECUTE format('SELECT * FROM %I WHERE col1 = $1',tabname) USING keyvalue;
В данном примере имя таблицы вставляется в запрос с помощью функции
format(). Значение для сравнения с полем col1
вставляется через USING параметр, поэтому оно не требует
заключения в кавычки.
OPENbound_cursorvar[ ( [argument_name:= ]argument_value[, ...] ) ];
Эта форма оператора OPEN используется для открытия переменной
курсора, запрос которой был привязан к ней при объявлении. Этот
курсор не должен быть уже открыт. Список выражений для фактических значений аргументов
должен присутствовать тогда и только тогда, когда курсор был объявлен с
принимать аргументы. Эти значения будут подставлены в запрос.
План выполнения запроса для связанного курсора всегда считается кэшируемым;
отсутствует эквивалент EXECUTE в данном случае.
Следует отметить, что SCROLL и NO SCROLL не могут быть
указаны в OPEN, так как режим прокрутки курсора
уже был определен.
Значения аргументов можно передавать с использованием либо позиционной
или именованной нотации. При позиционной
нотации все аргументы указываются по порядку. При именованной нотации
имя каждого аргумента указывается с использованием := для
отделять его от выражения аргумента. Подобно вызову
функций, описанному в Раздел 2.1.3, также
допускается смешивание позиционной и именованной нотаций.
Примеры (в них используются примеры объявления курсоров, приведенные выше):
OPEN curs2; OPEN curs3(42); OPEN curs3(key := 42);
Поскольку подстановка переменных выполняется в запросе связанного курсора,
существует два способа передачи значений в курсор: либо
с помощью явного аргумента, OPEN, либо неявно путем
ссылки на PL/pgSQL переменную в запросе.
Однако подставляться будут только те переменные, которые были объявлены до
объявления связанного курсора. В любом случае передаваемое значение
определяется в момент открытия курсора OPEN.
Например, другой способ получить тот же результат, что и в примере с
curs3 выше, заключается в следующем:
DECLARE
key integer;
curs4 CURSOR FOR SELECT * FROM tenk1 WHERE unique1 = key;
BEGIN
key := 42;
OPEN curs4;
После открытия курсора им можно управлять с помощью описанных здесь инструкций.
Данные операции не обязательно должны выполняться в той же функции, в которой изначально был открыт курсор. Можно вернуть значение типа refcursor
из функции, что позволит вызывающей стороне работать с данным курсором.
(На внутреннем уровне значение типа refcursor представляет собой просто строковое имя
портала, содержащего активный запрос для курсора. Данное имя
можно передавать, присваивать другим refcursor переменным
и так далее, не затрагивая сам портал.)
Все порталы неявно закрываются в конце транзакции. Следовательно,
значение refcursor типа пригодно для обращения к открытому курсору только до завершения текущей транзакции.
FETCH #FETCH [направление{ FROM | IN } ]курсорINTOtarget;
FETCH извлекает следующую строку (в указанном направлении) из курсора в целевой объект, которым может быть переменная строки, переменная типа record или список простых переменных через запятую, так же как
SELECT INTO. Если подходящей строки нет, целевому объекту присваивается значение NULL. Как и в случае с SELECT
INTO, можно проверить специальную переменную FOUND , чтобы определить, была ли получена строка. Если строка не получена, курсор позиционируется после последней строки или перед первой строкой в зависимости от направления перемещения.
Предложение направление может быть любым из вариантов, допустимых в SQL-команде FETCH
командами, за исключением тех, которые могут возвращать более одной строки; а именно, это может быть
NEXT,
PRIOR,
FIRST,
LAST,
ABSOLUTE count,
RELATIVE count,
FORWARD, или
BACKWARD.
Пропуск направление эквивалентно указанию NEXT.
В формах, использующих count,
параметр count может быть любым целочисленным выражением (в отличие от команды SQL FETCH ,
которая допускает только целочисленную константу).
направление значения, требующие перемещения
назад, скорее всего, приведут к ошибке, если курсор не был объявлен или открыт
с опцией SCROLL option.
курсор должно быть именем refcursor
переменной, ссылающейся на открытый портал курсора.
Примеры:
FETCH curs1 INTO rowvar; FETCH curs2 INTO foo, bar, baz; FETCH LAST FROM curs3 INTO x, y; FETCH RELATIVE -2 FROM curs4 INTO x;
MOVE #MOVE [направление{ FROM | IN } ]курсор;
MOVE перемещает курсор без извлечения каких-либо данных. MOVE работает так же, как команда
FETCH , за исключением того, что она только изменяет позицию
курсора и не возвращает строку, к которой был осуществлен переход.
Команда направление может быть любым из вариантов, допустимых в SQL-команде FETCH
, включая те, которые могут извлекать более одной строки; при этом
курсор позиционируется на последней такой строке.
(Однако в случае, когда направление
предложение представляет собой просто count выражение без ключевого слова считается устаревшим в PL/pgSQL.
Данный синтаксис допускает неоднозначность в случае, когда
предложение направление полностью опущено, и, следовательно, возможен сбой, если count не является константой.)
Как и в случае с SELECT
INTO, можно проверить специальную переменную FOUND можно проверить на предмет наличия строки для перехода. Если такая строка отсутствует, курсор позиционируется после последней строки или перед первой строкой в зависимости от направления перемещения.
Примеры:
MOVE curs1; MOVE LAST FROM curs3; MOVE RELATIVE -2 FROM curs4; MOVE FORWARD 2 FROM curs4;
UPDATE/DELETE WHERE CURRENT OF #UPDATEтаблицаSET ... WHERE CURRENT OFкурсор; DELETE FROMтаблицаWHERE CURRENT OFкурсор;
Когда курсор позиционирован на строке таблицы, эту строку можно обновить
или удалить, используя данный курсор для идентификации строки. Существуют
ограничения относительно того, каким может быть запрос курсора (в частности,
группировка отсутствует), и рекомендуется использовать FOR UPDATE в
курсоре. Для получения дополнительной информации см.
DECLARE
страницу справки.
Пример:
UPDATE foo SET dataval = myval WHERE CURRENT OF curs1;
CLOSE #
CLOSE курсор;
CLOSE закрывает портал, лежащий в основе открытого
курсоре. Это можно использовать для освобождения ресурсов до завершения
транзакции или для освобождения переменной курсора для ее повторного открытия.
Пример:
CLOSE curs1;
PL/pgSQL функции могут возвращать курсоры вызывающей стороне. Это удобно использовать для возврата нескольких строк или столбцов, особенно при работе с очень большими наборами результатов. Для этого функция открывает курсор и возвращает его имя вызывающей стороне (или просто открывает курсор, используя имя портала, заданное вызывающей стороной или известное ей иным образом). После этого вызывающая сторона может извлекать строки из курсора. Курсор может быть закрыт вызывающей стороной или будет закрыт автоматически при завершении транзакции.
Имя портала, используемое для курсора, может быть задано
программистом или сформировано автоматически. Чтобы задать имя портала,
необходимо просто присвоить строку refcursor переменной перед
его открытием. Строковое значение этой refcursor переменной
будет использовано OPEN в качестве имени нижележащего портала.
Однако, если refcursor значение переменной равно null
(как это и будет по умолчанию), то
OPEN автоматически генерирует имя, которое не
конфликтует с именами существующих порталов, и присваивает его
refcursor переменной.
До версии Digital Q.DataBase 16 переменные связанного курсора инициализировались собственными именами вместо значения null, чтобы имя соответствующего портала по умолчанию совпадало с именем переменной курсора. Это поведение было изменено, так как существовал высокий риск возникновения конфликтов между одноименными курсорами в различных функциях.
В следующем примере показан один из способов передачи имени курсора из вызывающей программы:
CREATE TABLE test (col text);
INSERT INTO test VALUES ('123');
CREATE FUNCTION reffunc(refcursor) RETURNS refcursor AS '
BEGIN
OPEN $1 FOR SELECT col FROM test;
RETURN $1;
END;
' LANGUAGE plpgsql;
BEGIN;
SELECT reffunc('funccursor');
FETCH ALL IN funccursor;
COMMIT;
В следующем примере используется автоматическая генерация имени курсора:
CREATE FUNCTION reffunc2() RETURNS refcursor AS '
DECLARE
ref refcursor;
BEGIN
OPEN ref FOR SELECT col FROM test;
RETURN ref;
END;
' LANGUAGE plpgsql;
-- использование курсоров требует выполнения в рамках транзакции.
BEGIN;
SELECT reffunc2();
reffunc2
--------------------
(1 row)
FETCH ALL IN "";
COMMIT;
В следующем примере показан один из способов возврата нескольких курсоров из одной функции:
CREATE FUNCTION myfunc(refcursor, refcursor) RETURNS SETOF refcursor AS $$
BEGIN
OPEN $1 FOR SELECT * FROM table_1;
RETURN NEXT $1;
OPEN $2 FOR SELECT * FROM table_2;
RETURN NEXT $2;
END;
$$ LANGUAGE plpgsql;
-- использование курсоров требует выполнения в рамках транзакции.
BEGIN;
SELECT * FROM myfunc('a', 'b');
FETCH ALL FROM a;
FETCH ALL FROM b;
COMMIT;
Существует вариант оператора FOR , позволяющий итерировать строки, возвращаемые курсором. Синтаксис:
[ <<label>> ] FORrecordvarINbound_cursorvar[ ( [argument_name:= ]argument_value[, ...] ) ] LOOPstatementsEND LOOP [label];
Переменная курсора должна быть привязана к определенному запросу при ее объявлении, и она не может быть уже открытой. Оператор
FOR автоматически открывает курсор и закрывает его при выходе из цикла. Список выражений для фактических значений аргументов должен указываться только в том случае, если курсор был объявлен с параметрами. Эти значения будут подставлены в запрос точно так же, как при выполнении OPEN (см. Раздел 5.6.7.2.3).
Переменная recordvar автоматически определяется как тип данных record и существует только внутри цикла (любое существующее определение переменной с тем же именем внутри цикла игнорируется). Каждая строка, возвращаемая курсором, последовательно присваивается этой переменной типа record, после чего выполняется тело цикла.