光标检索在 plpgsql 函数中创建的表中已删除的行

Posted

技术标签:

【中文标题】光标检索在 plpgsql 函数中创建的表中已删除的行【英文标题】:Cursor retrieves a deleted row in table created within plpgsql function 【发布时间】:2015-08-31 13:52:36 【问题描述】:

在 plpgsql 函数中,我创建了一个表并使用游标访问其行。在第一行时,我删除了下一个,令人惊讶的是(至少对我而言)游标获取了它。在同一个函数中重复时,它会按我的预期工作。

但是,如果表预先存在并且未在函数中创建,则永远不会提取已删除的行。

我错过了什么?

DECLARE
curs1 refcursor;
rec record;

BEGIN
CREATE TABLE test as select generate_series(1,5,1) test_id;

OPEN curs1 FOR SELECT * FROM test ORDER BY test_id; 
  LOOP
    FETCH curs1 INTO rec;
    EXIT WHEN NOT FOUND;
    RAISE NOTICE 'ROW:%',rec.test_id;
    IF rec.test_id=1 THEN
      DELETE FROM TEST WHERE test_id=3;
    END IF;
  END LOOP;
CLOSE curs1;

RAISE NOTICE 'AGAIN';
--just repeating without deleting

OPEN curs1 FOR SELECT * FROM test ORDER BY test_id; 
  LOOP
    FETCH curs1 INTO rec;
    EXIT WHEN NOT FOUND;
    RAISE NOTICE 'ROW:%',rec.test_id;
  END LOOP;
CLOSE curs1;

输出是:

NOTICE:  ROW:1
NOTICE:  ROW:2
NOTICE:  ROW:3
NOTICE:  ROW:4
NOTICE:  ROW:5
NOTICE:  AGAIN
NOTICE:  ROW:1
NOTICE:  ROW:2
NOTICE:  ROW:4
NOTICE:  ROW:5

【问题讨论】:

【参考方案1】:

原因是 Postgres 游标默认是“不敏感的”。 The documentation:

SQL 标准说它是否依赖于实现 游标对底层数据的并发更新很敏感 默认。 在 PostgreSQL 中,游标默认是不敏感的,可以 通过指定FOR UPDATE 使其敏感。其他产品的工作方式可能不同。

我的大胆强调。

所以尝试使用FOR UPDATE 子句:

DO
$$
DECLARE
   curs1 refcursor;
   rec record;
BEGIN
   CREATE TABLE test AS SELECT generate_series(1,5) test_id;

   OPEN curs1 FOR SELECT * FROM test ORDER BY test_id FOR UPDATE; 
   DELETE FROM test WHERE test_id = 3;
   LOOP
      FETCH curs1 INTO rec;
      EXIT WHEN NOT FOUND;
      RAISE NOTICE 'ROW:%',rec.test_id;
   END LOOP;
   CLOSE curs1;
END
$$

你会得到:

NOTICE:  ROW:1
NOTICE:  ROW:2
NOTICE:  ROW:4
NOTICE:  ROW:5

第 3 行不再可见。

【讨论】:

以上是关于光标检索在 plpgsql 函数中创建的表中已删除的行的主要内容,如果未能解决你的问题,请参考以下文章

plpgsql光标在unnest函数上

readtable() 函数正在从我的表中删除前三行

如何将表行传递给 plpgsql 函数?

从使用 MySql 创建的表中获取 ROW ID 以与 OnClick 函数一起使用

在 Lightswitch 中创建的不需要的表

删除在 DLL 中创建的对象