在plpgsql中获取光标

Posted

技术标签:

【中文标题】在plpgsql中获取光标【英文标题】:Fetch cursor in plpgsql 【发布时间】:2013-01-31 19:49:40 【问题描述】:

阅读此链接http://microjet.ath.cx/WebWiki/ResultPaginationWithPostgresql.html 后,我决定使用光标进行分页。但似乎我不知道如何在 plpgsql 中获取结果。

这是我的功能

CREATE OR REPLACE FUNCTION get_pagination_custom_word_moderation(_moderation_id bigint, _is_black boolean, _index integer, _max_result integer)
    RETURNS TABLE(word_id bigint,
        word character varying,
        is_num_rlpcm boolean,
        is_word_bund boolean,
        note text,
        create_time timestamp without time zone,
        last_update timestamp without time zone) AS
$BODY$
DECLARE custom_word_moderation_cursor CURSOR FOR
    SELECT
        word_id,
        word,
        is_num_rlpcm,
        is_word_bund,
        note,
        create_time,
        last_update
    FROM
        custom_word_moderation
    WHERE
        moderation_id=_moderation_id
    AND is_black=_is_black;
BEGIN

MOVE ABSOLUTE _index FROM custom_word_moderation_cursor;
RETURN QUERY FETCH _max_result FROM custom_word_moderation_cursor;

END;
$BODY$
  LANGUAGE plpgsql VOLATILE;

错误是:

ERROR:  syntax error at or near "$1"
LINE 1:  FETCH  $1  FROM  $2 
                ^
QUERY:   FETCH  $1  FROM  $2 
CONTEXT:  SQL statement in PL/PgSQL function "get_pagination_custom_word_moderation" near line 18

********** Error **********

ERROR: syntax error at or near "$1"
SQL state: 42601
Context: SQL statement in PL/PgSQL function "get_pagination_custom_word_moderation" near line 18

我认为问题在于如何返回获取结果表单游标。

【问题讨论】:

【参考方案1】:

您尝试执行的操作未实现。游标是按原样返回的,以便 client 可以随心所欲地获取行。特别是对于大的结果。为此,您可以使用 RETURNS refcursor 定义一个函数。

可以使其与FOR LOOPOUT 变量的显式分配一起工作,但与RETURNS TABLE 结合起来很棘手...... 您还必须OPEN the cursor,因为DECLARE 在plpgsql 上下文中的含义与SQL DECLARE for cursors 相同的关键字具有不同的含义。你必须FETCH .. INTO ..

改为使用不带光标的简单等效项:

CREATE OR REPLACE FUNCTION get_pagination_custom_word_moderation(
                         _moderation_id bigint, _is_black boolean
                       , _index integer, _max_result integer)
    RETURNS TABLE(word_id bigint,
        word varchar,
        is_num_rlpcm boolean,
        is_word_bund boolean,
        note text,
        create_time timestamp,
        last_update timestamp) AS
$func$
BEGIN
   RETURN QUERY
   SELECT word_id
         ,word
         ,is_num_rlpcm
         ,is_word_bund
         ,note
         ,create_time
         ,last_update
   FROM   custom_word_moderation
   WHERE  moderation_id = _moderation_id
   AND    is_black = _is_black
   OFFSET _index
   LIMIT  _max_result;
END
$func$ LANGUAGE plpgsql;

使用 SQL 函数甚至更简单:

CREATE OR REPLACE FUNCTION get_pagination_custom_word_moderation(
                         _moderation_id bigint, _is_black boolean
                       , _index integer, _max_result integer)
    RETURNS TABLE(word_id bigint,
        word varchar,
        is_num_rlpcm boolean,
        is_word_bund boolean,
        note text,
        create_time timestamp,
        last_update timestamp) AS
$func$
   SELECT word_id
         ,word
         ,is_num_rlpcm
         ,is_word_bund
         ,note
         ,create_time
         ,last_update
   FROM   custom_word_moderation
   WHERE  moderation_id = $1
   AND    is_black = $2
   OFFSET $3
   LIMIT  $4;
$func$ LANGUAGE sql;

我在函数体中使用$n 表示法,因为在 9.2 版本之前的 SQL 函数中不能通过名称引用参数。

如果你真的想返回一个表的所有列,你可以进一步简化:

CREATE OR REPLACE FUNCTION get_pagination_custom_word_moderation(
                         _moderation_id bigint, _is_black boolean
                       , _index integer, _max_result integer)
    RETURNS SETOF custom_word_moderation AS
$func$
   SELECT *
   FROM   custom_word_moderation
   WHERE  moderation_id = $1
   AND    is_black = $2
   OFFSET $3
   LIMIT  $4;
$func$ LANGUAGE sql;

【讨论】:

嗨 Erwin,感谢您的回复,是的,我完全错误地使用光标的方式,所以我修改我的函数以返回 refcursor(如您所说),然后在调用后使用 FETCH(函数外)函数(我在 php 中作为事务执行此操作)。但是您使用标准 SELECT(not cursor) 的建议与此链接 microjet.ath.cx/WebWiki/ResultPaginationWithPostgresql.html 相矛盾。我只想提一下使用游标或标准 SELECT 时的性能差异。对不起我的英语不好:) @Ahmad:嗯,分页是一件复杂的事情。这一切都取决于你的情况。对于昂贵的查询,如果您可以为不同的客户端保持连接打开,则游标方法可能会提高性能。

以上是关于在plpgsql中获取光标的主要内容,如果未能解决你的问题,请参考以下文章

plpgsql光标在unnest函数上

如何在 plpgsql 函数中获取表的关键字段?

在plpgsql中循环数组维度

从 plpgsql 函数中获取 Java ResultSet 的返回值

PostgreSQL plpgsql 获取当前程序 oid

通过 RAW plpgsql 中的 UPDATE 语句获取受影响的行