读取postgresql函数创建的uncommited行?

时间:2017-07-18 09:17:51

标签: postgresql function transactions read-uncommitted

我编写了一个函数,根据参数在表mytable中创建一行,并返回创建的行的id。

不幸的是,如果我使用以下语句,SELECT将不返回任何行,就好像该函数的事务与SELECT的事务不同。

SELECT * FROM mytable WHERE id = createComplexRow(...);

我的理解是我为select和function运行了相同的事务,然后应该能够读取未提交的行。

我正在尝试使用postgres 9.6

任何线索如何让它正常工作?

1 个答案:

答案 0 :(得分:7)

让我们来看看发生了什么。

CREATE TABLE mytable(
   id serial PRIMARY KEY,
   val timestamp with time zone NOT NULL
);

CREATE FUNCTION createcomplexrow() RETURNS integer
   LANGUAGE SQL AS
'INSERT INTO mytable (val) VALUES (current_timestamp) RETURNING id';

该函数必须隐式VOLATILE,因为它修改了数据库。

让我们插入几行:

SELECT createcomplexrow();
SELECT createcomplexrow();

现在让我们试试你的陈述:

SELECT * FROM mytable WHERE id = createcomplexrow();

 id | val
----+-----
(0 rows)

确实,没有结果!

但表中有新值:

SELECT * FROM mytable;

 id |              val
----+-------------------------------
  1 | 2017-07-18 11:50:22.031922+02
  2 | 2017-07-18 11:50:23.640775+02
  3 | 2017-07-18 11:50:31.392773+02
  4 | 2017-07-18 11:50:31.392773+02
(4 rows)

要查看会发生什么,EXPLAIN查询:

EXPLAIN (COSTS off)
   SELECT * FROM mytable WHERE id = createcomplexrow();

             QUERY PLAN
-------------------------------------
 Seq Scan on mytable
   Filter: (id = createcomplexrow())
(2 rows)

PostgreSQL扫描表格,对于找到的每一行,它调用该函数并将结果与​​行的id进行比较。

当它用id = 1扫描行时,该函数将返回3(并插入一行)。因此,跳过id = 1的行。

同样,系统会跳过包含id = 2的行,并会创建一个id = 4的新行。

现在为什么执行会在这里停止而不是继续扫描两个新创建的行?

这些lines from the documentation解释了一下:

  

实际上,SELECT查询会在查询开始运行的瞬间看到数据库的快照。   但是,SELECT确实会在自己的事务中看到以前的更新的影响,即使它们尚未提交。

(强调我的)

该语句没有看到该函数的效果,因为该函数不是在以前的更新中执行,而是在同一语句中执行{{1} }。

(在data modifying WITH queries中也是如此;您可能会发现阅读文档中的那部分内容很有启发性。)

实际上,你应该很高兴它以这种方式处理,否则你最终会得到一个无限循环,继续在表格中插入行。