8

我正在调整一些 PL/pgSQL 代码,以便refcursor可以将表名作为参数。因此我更改了以下行:

declare
 pointCurs CURSOR FOR SELECT * from tableName for update;

有了这个:

OPEN pointCurs FOR execute 'SELECT * FROM ' || quote_ident(tableName) for update;

我调整了循环,瞧,循环通过了。现在在循环中的某个时刻,我需要更新记录(由光标指向),但我被卡住了。我应该如何正确调整以下代码行?

UPDATE tableName set tp_id = pos where current of pointCurs;

我修复了 and 的引号tableName并在开头pos添加了EXECUTE子句,但我在where current of pointCurs.

问题:

  1. 如何更新记录?
  2. 该函数对来自公共模式的表正常工作,而对来自其他模式的表(例如,trace.myname)失败。

任何意见都非常感谢..

4

1 回答 1

17

(i) 的答案

1.显式(未绑定)游标

EXECUTE不是“子句”,而是执行 SQL 字符串的 PL/pgSQL 命令。光标在命令中不可见。您需要将值传递给它。

因此,您不能使用特殊语法WHERE CURRENT OFcursor。我使用系统列ctid来确定行而不知道唯一列的名称。请注意,ctid仅保证在同一事务中稳定。

CREATE OR REPLACE FUNCTION f_curs1(_tbl text)
  RETURNS void AS
$func$
DECLARE
   _curs refcursor;
   rec record;
BEGIN
   OPEN _curs FOR EXECUTE 'SELECT * FROM ' || quote_ident(_tbl) FOR UPDATE;

   LOOP
      FETCH NEXT FROM _curs INTO rec;
      EXIT WHEN rec IS NULL;

      RAISE NOTICE '%', rec.tbl_id;

      EXECUTE format('UPDATE %I SET tbl_id = tbl_id + 10 WHERE ctid = $1', _tbl)
      USING rec.ctid;
   END LOOP;
END
$func$  LANGUAGE plpgsql;

为什么format()%I

还有一个FOR语句的变体循环游标,但它只适用于绑定游标。我们必须在这里使用一个未绑定的游标。

2. 隐式游标在FOR循环中

在 plpgsql中通常不需要显式游标。改为使用FOR循环的隐式游标:

CREATE OR REPLACE FUNCTION f_curs2(_tbl text)
  RETURNS void AS
$func$
DECLARE
   _ctid tid;
BEGIN
   FOR _ctid IN EXECUTE 'SELECT ctid FROM ' || quote_ident(_tbl) FOR UPDATE
   LOOP
      EXECUTE format('UPDATE %I SET tbl_id = tbl_id + 100 WHERE ctid = $1', _tbl)
      USING _ctid;
   END LOOP;
END
$func$  LANGUAGE plpgsql;

3. 基于集合的方法

或者更好,但是(如果可能的话!):根据基于集合的操作重新考虑您的问题并执行单个(动态)SQL 命令:

-- Set-base dynamic SQL
CREATE OR REPLACE FUNCTION f_nocurs(_tbl text)
  RETURNS void AS
$func$
BEGIN
   EXECUTE format('UPDATE %I SET tbl_id = tbl_id + 1000', _tbl);
   -- add WHERE clause as needed
END
$func$  LANGUAGE plpgsql;

SQL Fiddle演示了所有 3 个变体。

(ii) 的答案

一个模式限定的表名trace.myname实际上由两个标识符组成。你必须

  • 要么分别通过和逃脱它们,
  • 或者采用更优雅的使用regclass类型的方法:
CREATE OR REPLACE FUNCTION f_nocurs(_tbl regclass)
  RETURNS void AS
$func$
BEGIN
   EXECUTE format('UPDATE %s SET tbl_id = tbl_id + 1000', _tbl);
END
$func$  LANGUAGE plpgsql;

我从 切换%I%s,因为regclass当(自动)转换为 时,参数会自动正确转义text
此相关答案中的更多详细信息:

于 2013-09-16T16:54:33.110 回答