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

问题描述 投票:0回答:1

我正在调整一些 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;

我修复了

tableName
pos
的引号,并在开头添加了
EXECUTE
子句,但我在
where current of pointCurs
上收到了错误。

问题:

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

任何意见都非常感谢..

postgresql sql-update plpgsql dynamic-sql database-cursor
1个回答
17
投票

回答1。

1.显式(未绑定)光标

EXECUTE
不是一个“子句”,而是一个执行SQL字符串的PL/pgSQL命令。光标在命令内不可见。您需要向其传递值。

因此,您不能使用特殊语法

WHERE CURRENT OF
cursor
。我使用系统列
ctid
来确定行,而不知道唯一列的名称。请注意,
ctid
仅保证在同一事务内稳定,并且仅在单个物理表内唯一(无继承或分区)。参见:

CREATE OR REPLACE FUNCTION f_curs1(_tbl text)
  RETURNS void
  LANGUAGE plpgsql 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$;

为什么

format()
%I

还有一个

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

2.
FOR
循环中的隐式光标

PL/pgSQL 中通常不需要显式游标。请改用

FOR
循环的隐式游标:

CREATE OR REPLACE FUNCTION f_curs2(_tbl text)
  RETURNS void
  LANGUAGE plpgsql 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$;

3.基于集合的方法

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

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

SQL Fiddle 演示所有 3 个变体。

回答2。

trace.myname
这样的模式限定表名实际上由 two 标识符组成。你必须

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

%I

 切换到 
%s
,因为 
regclass
 参数在(自动)转换为 
text
 时会自动正确转义。参见:

  • 表名作为 PostgreSQL 函数参数
© www.soinside.com 2019 - 2024. All rights reserved.