pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
和一次执行整个查询不同,可以建立一个游标来封装该查询,并且接着一次读取该查询结果的一些行。这样做的原因之一是在结果中包含大量行时避免内存不足(不过,PL/pgSQL用户通常不需要担心这些,因为FOR循环在内部会自动使用一个游标来避免内存问题)。一种更有趣的用法是返回一个函数已经创建的游标的引用,允许调用者读取行。这提供了一种有效的方法从函数中返回大型行集。
所有在PL/pgSQL中对游标的访问都会通过游标变量,它总是特殊的数据类型refcursor。创建游标变量的一种方法是把它声明为一个类型为refcursor的变量。另外一种方法是使用游标声明语法,通常是:
name[ [ NO ] SCROLL ] CURSOR [ (arguments) ] FORquery;
(为兼容 Oracle,可以用 IS 代替 FOR。)如果指定了 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选项与已绑定游标中的含义相同。
一个示例:
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。注意,不能指定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 选项声明或打开。
示例:
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 参考页。
示例:
FETCH curs1 INTO rowvar; FETCH curs2 INTO foo, bar, baz; FETCH LAST FROM curs3 INTO x, y; FETCH RELATIVE -2 FROM curs4 INTO x;
CLOSE
CLOSE cursor;
CLOSE关闭打开游标所基于的 portal。这样就可以在事务结束之前提前释放资源,或者释放该游标变量以便再次打开。
示例:
MOVE curs1; MOVE LAST FROM curs3; MOVE RELATIVE -2 FROM curs4; MOVE FORWARD 2 FROM curs4;
PL/pgSQL函数可以向调用者返回游标。这对于返回多行或多列,特别是非常大的结果集时很有用。要做到这一点,函数需要打开游标,并把游标名返回给调用者(或者直接使用调用者指定或已知的 portal 名称来打开游标)。随后调用者就可以从该游标中提取行。游标既可以由调用者关闭,也会在事务结束时自动关闭。
游标使用的 portal 名称既可以由程序员指定,也可以自动生成。要指定 portal 名称,只需在打开refcursor变量之前给它赋一个字符串值。OPEN会把该refcursor变量的字符串值用作底层 portal 的名称。不过,如果refcursor变量为 null,OPEN就会自动生成一个与任何现有 portal 都不冲突的名称,并把它赋回给refcursor变量。
已绑定游标变量会被初始化为表示其名称的字符串值,因此除非程序员在打开游标之前通过赋值覆盖它,否则 portal 名称与游标变量名相同。而未绑定游标变量在初始时默认为空值,因此除非被覆盖,否则它会得到一个自动生成的唯一名称。
一个示例:
CLOSE curs1;
下面的示例使用了自动游标名生成:
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
--------------------
<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;
-- 需要在一个事务中使用游标。
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 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。