37.8. 游标 #
不是一次执行整个查询,而是可以建立一个封装该查询的游标,然后每次读取查询结果的少数几行。这样做的一个原因是避免结果包含大量行时的内存溢出。(不过,
PL/pgSQL 用户通常不需要担心这一点,因为
FOR 循环内部自动使用游标来避免内存问题。)更有趣的用法是返回函数所创建游标的引用,让调用者读取这些行。这提供了一种从函数返回大型行集的高效方式。
37.8.1. 声明游标变量 #
PL/pgSQL 中对游标的所有访问都通过游标变量进行,游标变量总是属于特殊数据类型 refcursor。创建游标变量的一种方法是直接把它声明为 refcursor 类型的变量。另一种方法是使用游标声明语法,其一般形式为:
nameCURSOR [ (arguments) ] FORquery;
(为兼容 Oracle,FOR 可以换成
IS。)如果指定了 arguments,它是一个由 对组成的逗号分隔列表,这些名称在给定查询中将被参数值替换。替换这些名称的实际值将在游标打开时指定。
name
datatype
一些例子:
DECLARE
curs1 refcursor;
curs2 CURSOR FOR SELECT * FROM tenk1;
curs3 CURSOR (key integer) IS SELECT * FROM tenk1 WHERE unique1 = key;
这三个变量的数据类型都是 refcursor,但第一个可用于任何查询,第二个已有完全指定的查询绑定于其上,最后一个有一个参数化查询绑定于其上。(打开游标时,key 将被替换为一个整数参数值。)变量 curs1 被称为未绑定的,因为它没有绑定到任何特定查询。
37.8.2. 打开游标 #
游标可以用来检索行之前,必须先被打开。(这等价于 SQL
命令 DECLARE CURSOR。)PL/pgSQL
有三种形式的 OPEN 语句,其中两种使用未绑定游标变量,第三种使用绑定游标变量。
37.8.2.1. OPEN FOR SELECT
OPEN unbound-cursor FOR SELECT ...; 游标变量被打开并被给定要执行的指定查询。游标不能已经处于打开状态,而且它必须被声明为未绑定游标(即简单的
refcursor 变量)。SELECT 查询的处理方式与 PL/pgSQL 中其他
SELECT 语句相同:替换
PL/pgSQL 变量名,并缓存查询计划供可能的重用。
一个例子:
OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey;
37.8.2.2. OPEN FOR EXECUTE
OPENunbound-cursorFOR EXECUTEquery-string;
游标变量被打开并被给定要执行的指定查询。游标不能已经处于打开状态,而且它必须被声明为未绑定游标(即简单的
refcursor 变量)。查询以与 EXECUTE
命令中相同的方式指定为字符串表达式。与通常一样,这提供了灵活性,使查询可以随每次运行而变化。
一个例子:
OPEN curs1 FOR EXECUTE ''SELECT * FROM '' || quote_ident($1);
37.8.2.3. 打开绑定游标
OPENbound-cursor[ (argument_values) ];
这种形式的 OPEN 用于打开其查询在声明时就绑定于其上的游标变量。游标不能已经处于打开状态。当且仅当游标被声明为接受参数时,必须出现实参值表达式列表。这些值将在查询中被替换。绑定游标的查询计划总是被视为可缓存的;这种情况没有
EXECUTE 的等价物。
例子:
OPEN curs2; OPEN curs3(42);
37.8.3. 使用游标 #
游标一旦被打开,就可以用这里描述的语句操纵它。
这些操纵不必发生在最初打开游标的同一个函数中。你可以从函数中返回
refcursor 值,让调用者操作该游标。(在内部,refcursor 值只是包含该游标活动查询的所谓门廊的字符串名称。这个名称可以被传递、赋给其他 refcursor 变量等等,而不会干扰门廊。)
所有门廊都在事务结束时隐式关闭。因此 refcursor 值只在事务结束之前可用于引用打开的游标。
37.8.3.1. FETCH
FETCHcursorINTOtarget;
FETCH 从游标检索下一行到目标中,目标可以是行变量、记录变量或简单变量的逗号分隔列表,就像
SELECT INTO 一样。与 SELECT
INTO 一样,可以检查特殊变量 FOUND
来看是否取得了行。
一个例子:
FETCH curs1 INTO rowvar; FETCH curs2 INTO foo, bar, baz;
37.8.3.2. CLOSE
CLOSE cursor; CLOSE 关闭打开游标底层的门廊。这可用于在事务结束之前释放资源,或释放游标变量以便再次打开。
一个例子:
CLOSE curs1;
37.8.3.3. 返回游标
PL/pgSQL 函数可以向调用者返回游标。这对返回多行或多列很有用,尤其是对于非常大的结果集。为此,函数打开游标并把游标名返回给调用者(或者只是用调用者指定的或以其他方式为调用者所知的门廊名打开游标)。然后调用者可以从游标中获取行。游标可以由调用者关闭,也可以在事务关闭时自动关闭。
游标使用的门廊名可以由程序员指定,也可以自动生成。要指定门廊名,只需在打开游标之前把一个字符串赋给 refcursor 变量。OPEN 会把 refcursor 变量的字符串值用作底层门廊的名称。但如果 refcursor 变量为空,OPEN 会自动生成一个不与任何现有门廊冲突的名称,并把它赋给该 refcursor 变量。
注意
绑定游标变量被初始化为表示其名称的字符串值,因此门廊名与游标变量名相同,除非程序员在打开游标之前通过赋值覆盖它。但未绑定游标变量初始默认为空值,因此除非被覆盖,它会得到一个自动生成的唯一名称。
下面的例子展示了由调用者提供游标名的一种方式:
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
--------------------
<unnamed cursor 1>
(1 row)
FETCH ALL IN "<unnamed cursor 1>";
COMMIT;