1

I wrote a function that creates a row in the table mytable accordingly to the parameters and returns the id of the created row.

Unfortunately, if I use the following statement, the SELECT returns no row, as if the transaction of the function was different from the one of the SELECT.

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

My understanding was that I was running the same transaction for the select and the function and then should be able to read uncommitted rows.

I am trying with postgres 9.6

Any clue how to make it works properly ?

4

1 回答 1

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并创建一个新的带有的行。

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

文档中的这些行在某种程度上解释了它:

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

(强调我的)

该语句看不到函数的效果,因为该函数不是在之前的 update中执行,而是在SELECT.

(在数据修改WITH查询中也会发生同样的情况;您可能会发现阅读文档的那部分很有启发性。)

实际上,您应该很高兴以这种方式处理它,否则您最终会陷入无限循环,继续将行插入表中。

于 2017-07-18T10:11:00.957 回答