更新表名为参数的游标记录

art*_*hur 6 postgresql dynamic-sql plpgsql cursor sql-update

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

declare
 pointCurs CURSOR FOR SELECT * from tableName for update;
Run Code Online (Sandbox Code Playgroud)

这一个:

OPEN pointCurs FOR execute 'SELECT * FROM ' || quote_ident(tableName) for update;
Run Code Online (Sandbox Code Playgroud)

我调整了循环,然后,循环经过了.现在在循环中的某个时刻我需要更新记录(由光标指向)并且我卡住了.我该如何正确调整以下代码行?

UPDATE tableName set tp_id = pos where current of pointCurs;
Run Code Online (Sandbox Code Playgroud)

我修改了tableName和的引号,并在开头pos添加了EXECUTE子句,但是我得到了错误where current of pointCurs.

问题:

(i)我如何更新记录?
(ii)该函数适用于来自公共模式的表,而来自其他模式的表(例如trace.myname)失败.

任何评论都非常感谢..

Erw*_*ter 15

回答(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;
Run Code Online (Sandbox Code Playgroud)

为什么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;
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

我切换%I%s,因为regclass参数在(自动)转换为时自动正确转义text.
这个相关答案的更多细节: