游标

游标是对查询结果集的引用,允许您一次查看一行结果。游标记住结果集中的位置,可以是以下位置之一:

  • 结果行

  • 第一行之前

  • 最后一行之后

您还可以使用 FOR 循环在未打开的绑定游标上进行迭代。有关详细信息,请参阅控制流

声明游标

绑定游标

要将游标绑定到声明中的语句,请使用 FOR 关键字:

cursor_name CURSOR [ ( arg_name arg_type [, ...] ) ] FOR statement;

游标的实参使您能够更好地控制要处理的行。例如,假设您有下表:

=> SELECT * FROM coordinates_xy;
 x | y
---+----
 1 |  2
 9 |  5
 7 | 13
...
(100000 rows)

如果仅对 y 为 6 的行感兴趣,则可以声明以下游标,然后在打开游标时提供实参 6

c CURSOR (key int) FOR SELECT * FROM coordinates_xy WHERE y=key;

未绑定的游标

要声明未绑定到特定查询的游标,请使用 refcursor 类型:

cursor_name refcursor;

您可以随时使用打开对未绑定的游标进行绑定。

例如,要声明游标 my_unbound_cursor

my_unbound_cursor refcursor;

打开和关闭游标

打开

打开游标将使用给定实参执行查询,并将游标放在结果集的第一行之前。查询结果的排序(因此结果集的开头)是不确定的,除非您指定了 ORDER BY 子句

打开绑定的游标

打开声明期间绑定的游标:

OPEN bound_cursor [ ( [ arg_name := ] arg_value [, ...] ) ];

例如,给定以下声明:

c CURSOR (key int) FOR SELECT * FROM t1 WHERE y=key;

可以使用以下某项操作打开游标:

OPEN c(5);
OPEN c(key := 5);

关闭

当游标离开范围时,打开的游标将自动关闭,但您可以使用“关闭”命令提前关闭游标。关闭的游标可以稍后重新打开,这将重新执行查询并准备新的结果集。

CLOSE cursor;

打开未绑定的游标

对未绑定的游标进行绑定,然后打开游标:

OPEN unbound_cursor FOR statement;

您还可以使用 EXECUTE,因为它是语句:

OPEN unbound_cursor FOR EXECUTE statement_string [ USING expression [, ... ] ];

例如,将游标 c 绑定到表 product_data 的查询:

OPEN c for SELECT * FROM product_data;

提取行

FETCH 语句:

  1. 检索指定游标当前指向的行,并将其存储在某个变量中。

  2. 使游标前进到下一个位置。

variable [, ...] := FETCH opened_cursor;

检索的值存储在变量中。行通常有多个值,因此您可以每行使用一个变量。

如果 FETCH 成功检索值,则特殊变量 FOUND 设置为 true。否则,如果您在游标经过结果集的最后一行时调用 FETCH,它将返回 NULL,且特殊变量 FOUND 设置为 false

下面的过程创建游标 c,将其绑定到 coordinates 表中的 SELECT 查询。该过程将实参 1 传递给游标,因此游标仅检索 y 坐标为 1 的行,将坐标存储在变量 x_y_z_ 中。

只有两行的 y 坐标为 1,因此使用 FETCH 两次后,第三个 FETCH 开始返回 NULL 值,且 FOUND 设置为 false

=> SELECT * FROM coordinates;
 x  | y | z
----+---+----
 14 | 6 | 19
  1 | 6 |  2
 10 | 6 | 39
 10 | 2 |  1
  7 | 1 | 10
 67 | 1 | 77
(6 rows)

DO $$
DECLARE
    c CURSOR (key int) FOR SELECT * FROM coordinates WHERE y=key;
    x_ int;
    y_ int;
    z_ int;
BEGIN
    OPEN c(1); -- only retrieve rows where y=1
    x_,y_,z_ := FETCH c;
    RAISE NOTICE 'cursor returned %, %, %, FOUND=%',x_, y_, z_, FOUND;
    x_,y_,z_ := FETCH c; -- fetches the last set of results and moves to the end of the result set
    RAISE NOTICE 'cursor returned %, %, %, FOUND=%',x_, y_, z_, FOUND;
    x_,y_,z_ := FETCH c; -- cursor has advanced past the final row
    RAISE NOTICE 'cursor returned %, %, %, FOUND=%',x_, y_, z_, FOUND;
END;
$$;

NOTICE 2005:  cursor returned 7, 1, 10, FOUND=t
NOTICE 2005:  cursor returned 67, 1, 77, FOUND=t
NOTICE 2005:  cursor returned <NULL>, <NULL>, <NULL>, FOUND=f

移动游标

MOVE 使打开的游标前进到下一个位置,而不检索该行。如果游标位置(在 MOVE 之前)未经过最后一行,则特殊 FOUND 变量设置为 true — 即,如果调用 FETCH 而不调用 MOVE,则会检索该行。

MOVE bound_cursor;

例如,此游标仅检索 y 坐标为 2 的行。结果集只有一行,因此使用 MOVE 两次会导致前进超过第一行(和最后一行),且将 FOUND 设置为 false:

 => SELECT * FROM coordinates WHERE y=2;
 x  | y | z
----+---+---
 10 | 2 | 1
(1 row)

DO $$
DECLARE
    c CURSOR (key int) FOR SELECT * FROM coordinates WHERE y=key;
BEGIN
    OPEN c(2); -- only retrieve rows where y=2, cursor starts before the first row
    MOVE c; -- cursor advances to the first (and last) row
    RAISE NOTICE 'FOUND=%', FOUND; -- FOUND is true because the cursor points to a row in the result set
    MOVE c; -- cursor advances past the final row
    RAISE NOTICE 'FOUND=%', FOUND; -- FOUND is false because the cursor is past the final row
END;
$$;

NOTICE 2005:  FOUND=t
NOTICE 2005:  FOUND=f