pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
和一次执行整个查询不同,可以建立一个 游标来封装该查询,并且接着一次读取该查询 结果的一些行。这样做的原因之一是在结果中包含大量行时避免内存 不足(不过,PL/pgSQL 用户通常不需要 担心这些,因为 FOR 循环在内部会自动使用一个 游标来避免内存问题)。一种更有趣的用法是返回一个函数已经创建 的游标的引用,允许调用者读取行。这提供了一种有效的方法从函数 中返回大型行集。
所有在 PL/pgSQL 中对游标的访问都会 通过游标变量,它总是特殊的数据类型 refcursor。创建游标变量的一种方法是把它声明为一个 类型为 refcursor 的变量。另外一种方法是使用游标声明 语法,通常是:
name[ [ NO ] SCROLL ] CURSOR [ (arguments) ] FORquery;
(FOR can be replaced by IS for Oracle compatibility.) 如果指定了 SCROLL,游标就支持向后滚动;如果指定了 NO SCROLL,向后提取会被拒绝;如果两者都未指定, 是否允许向后提取则取决于查询本身。如果指定了 arguments,它就是一个由 对组成的逗号分隔列表,这些名字会在给定查询中被参数值替换。实际 替换这些名字的值会在打开游标时提供。name datatype
Some examples:
DECLARE
curs1 refcursor;
curs2 CURSOR FOR SELECT * FROM tenk1;
curs3 CURSOR (key integer) IS SELECT * FROM tenk1 WHERE unique1 = key;
所有这三个变量都是 refcursor 类型,但是第一个可以 用于任何查询,而第二个已经被绑定了一个 完全指定的查询,并且最后一个被绑定了一个参数化查询。(游标被 打开时,key 将被一个整数参数值替换。)变量 curs1 被称为 未绑定,因为它没有被绑定到任何特定查询。
在能够使用游标检索行之前,必须先将其 打开(这等效于 SQL 命令 DECLARE CURSOR)。PL/pgSQL 有三种形式的 OPEN 命令,其中两种用于未绑定 游标变量,另一种用于已绑定游标变量。
可以通过 第 39.7.4 节 中描述的 FOR 语句在不显式打开游标的情况下使用已绑定 的游标变量。
OPEN FOR queryOPENunbound_cursorvar[ [ NO ] SCROLL ] FORquery;
该游标变量会被打开,并被赋予要执行的指定查询。该游标不能 已经处于打开状态,并且它必须已被声明为未绑定游标变量(即, 一个简单的 refcursor 变量)。该查询必须是 SELECT,或其他会返回行的命令(例如 EXPLAIN)。该查询会以与 PL/pgSQL 中其他 SQL 命令相同的 方式处理:替换 PL/pgSQL 变量名,并缓存查询计划 以备后续重用。当一个 PL/pgSQL 变量被替换到游标查询中时, 被替换的是它在 OPEN 时刻所具有的值;之后 对该变量的更改不会影响游标的行为。 SCROLL 和 NO SCROLL 选项与已绑定游标中的含义相同。
An example:
OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey;
OPEN FOR EXECUTEOPENunbound_cursorvar[ [ NO ] SCROLL ] FOR EXECUTEquery_string[ USINGexpression[, ... ] ];
该游标变量会被打开,并被赋予要执行的指定查询。该游标不能 已经处于打开状态,并且必须已被声明为未绑定游标变量(即, 一个简单的 refcursor 变量)。该查询以字符串 表达式的形式给出,这一点与 EXECUTE 命令相同。像往常一样,这提供了 灵活性,因此查询计划可以在不同执行之间变化(见 第 39.10.2 节),同时也意味着不会在 该命令字符串上执行变量替换。与 EXECUTE 一样,可以通过 USING 把参数值插入动态命令中。 SCROLL 和 NO SCROLL 选项与已绑定游标中的含义相同。
一个示例:
OPEN curs1 FOR EXECUTE 'SELECT * FROM ' || quote_ident(tabname)
|| ' WHERE col1 = $1' USING keyvalue;
在这个示例中,表名以文本方式插入到查询中,因此建议使用 quote_ident() 来防范 SQL 注入。 col1 的比较值通过 USING 参数插入,因此无需加引号。
OPENbound_cursorvar[ (argument_values) ];
这种形式的 OPEN 用于打开一个在声明时就已经绑定查询 的游标变量。该游标不能已经处于打开状态。当且仅当该游标被 声明为接收参数时,才必须提供实际参数值表达式列表。这些值 会被替换到查询中。 已绑定游标的查询计划始终被视为可缓存;在这种情况下没有与 EXECUTE 对应的形式。注意,不能在 OPEN 中指定 SCROLL 和 NO SCROLL,因为游标的滚动行为已经确定。
注意,因为会在已绑定游标的查询上执行变量替换,所以有两种 向游标传值的方式:要么给 OPEN 一个显式参数,要么在查询中隐式地 引用 PL/pgSQL 变量。但是,只有在 已绑定游标声明之前声明的变量才会被替换进去。无论哪种方式, 要传递的值都是在 OPEN 时刻确定的。
示例:
OPEN curs2; OPEN curs3(42);
一旦一个游标已经被打开,那么就可以用这里描述的语句操作它。
这些操作不必发生在最初打开该游标的同一个函数中。你可以从函数 中返回一个 refcursor 值,让调用者来操作该游标。 (在内部,refcursor 值只是一个所谓 portal 的字符串 名称,该 portal 包含了该游标的活动查询。这个名称可以被传递、 赋给其他 refcursor 变量等等,而不会干扰该 portal。)
所有 portal 都会在事务结束时被隐式关闭。因此, refcursor 值只能在事务结束之前用于引用一个打开的 游标。
FETCHFETCH [direction{ FROM | IN } ]cursorINTOtarget;
FETCH 从游标中检索下一行到目标中,目标可以 是一个行变量、记录变量或者逗号分隔的简单变量列表,就像 SELECT INTO 一样。如果没有下一行,目标会被 设置为 NULL。与 SELECT INTO 一样,可以检查特殊变量 FOUND 来看是否获得了一行。
direction 子句可以是 SQL FETCH 命令中允许的任何变体,除了那些能够 取得多于一行的。 即它可以是 NEXT、 PRIOR、 FIRST、 LAST、 ABSOLUTE count、 RELATIVE count、 FORWARD 或者 BACKWARD。 省略 direction 和指定 NEXT 是一样的。要求反向移动的 direction 值很可能会失败,除非游标 被使用 SCROLL 选项声明或打开。
cursor 必须是一个引用已打开游标 portal 的 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;
MOVEMOVE [direction{ FROM | IN } ]cursor;
MOVE 重新定位一个游标而不检索任何数据。 MOVE 的工作方式与 FETCH 命令完全一样,只是仅重新定位游标, 而不返回移动到的行。与 SELECT INTO 一样,可以检查特殊变量 FOUND 来看是否存在可移动到的下一行。
direction 子句可以是 SQL FETCH 命令中允许的任何变体,即 NEXT、 PRIOR、 FIRST、 LAST、 ABSOLUTE count、 RELATIVE count、 ALL、 FORWARD [ count | ALL ] 或 BACKWARD [ count | ALL ]。 省略 direction 和指定 NEXT 是一样的。要求反向移动的 direction 值很可能会失败,除非游标 被使用 SCROLL 选项声明或打开。
Examples:
MOVE curs1; MOVE LAST FROM curs3; MOVE RELATIVE -2 FROM curs4; MOVE FORWARD 2 FROM curs4;
UPDATE/DELETE WHERE CURRENT OFUPDATEtableSET ... WHERE CURRENT OFcursor; DELETE FROMtableWHERE CURRENT OFcursor;
当游标定位在某个表行上时,可以使用该游标来标识该行,并对其 执行更新或删除。游标查询的形式有一些限制(尤其不能包含 分组),并且在这类场景中最好对游标使用 FOR UPDATE。详见 DECLARE 参考页。
一个示例:
UPDATE foo SET dataval = myval WHERE CURRENT OF curs1;
CLOSE
CLOSE cursor;
CLOSE 关闭打开游标所基于的 portal。这样就可以在事务结束之前提前释放资源,或者释放该 游标变量以便再次打开。
一个示例:
CLOSE curs1;
PL/pgSQL 函数可以向调用者返回游标。 这对于返回多行或多列,特别是非常大的结果集时很有用。要做到 这一点,函数需要打开游标,并把游标名返回给调用者(或者直接 使用调用者指定或已知的 portal 名称来打开游标)。随后调用者 就可以从该游标中提取行。游标既可以由调用者关闭,也会在事务 结束时自动关闭。
游标使用的 portal 名称既可以由程序员指定,也可以自动生成。 要指定 portal 名称,只需在打开 refcursor 变量之前给它赋一个字符串值。 OPEN 会把该 refcursor 变量的字符串值用作底层 portal 的名称。 不过,如果 refcursor 变量为 null, OPEN 就会自动生成一个与任何现有 portal 都 不冲突的名称,并把它赋回给 refcursor 变量。
已绑定游标变量会被初始化为表示其名称的字符串值,因此除非 程序员在打开游标之前通过赋值覆盖它,否则 portal 名称与 游标变量名相同。而未绑定游标变量在初始时默认为空值,因此 除非被覆盖,否则它会得到一个自动生成的唯一名称。
下面的示例展示了由调用者提供游标名称的一种方式:
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;
-- need to be in a transaction to use cursors.
BEGIN;
SELECT reffunc2();
reffunc2
--------------------
<unnamed cursor 1>
(1 row)
FETCH ALL IN "<unnamed cursor 1>";
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;
-- need to be in a transaction to use cursors.
BEGIN;
SELECT * FROM myfunc('a', 'b');
FETCH ALL FROM a;
FETCH ALL FROM b;
COMMIT;
FOR 语句还有一种变体,允许循环通过游标返回 的行。语法是:
[ <<label>> ] FORrecordvarINbound_cursorvar[ (argument_values) ] LOOPstatementsEND LOOP [label];
该游标变量必须在声明时已经被绑定到某个查询,并且它 不能已经处于打开状态。 FOR 语句会自动打开游标,并且在退出循环时 自动关闭游标。当且仅当游标被声明要使用参数时,才必须出现一个 实际参数值表达式的列表。这些值会被替换到查询中,采用 OPEN 期间的方式。 变量 recordvar 会被自动定义为 record 类型,并且只存在于循环内部(循环中该变量名 任何已有定义都会被忽略)。每一个由游标返回的行都会被陆续地 赋值给这个记录变量并且执行循环体。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。