让我们看看发生了什么。
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 确实看到了在其自己的事务中执行的先前更新的效果,即使它们尚未提交。
(强调我的)
该语句看不到函数的效果,因为该函数不是在先前的更新中执行,而是在与SELECT相同的语句中执行。
(data modifying WITH queries 中也有同样的情况;您可能会发现阅读文档的那部分很有启发性。)
实际上,您应该很高兴以这种方式处理它,否则您最终会陷入无限循环,不断将行插入表中。