让我们看看发生了什么。
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
查询中也会发生同样的情况;您可能会发现阅读文档的那部分很有启发性。)
实际上,您应该很高兴以这种方式处理它,否则您最终会陷入无限循环,继续将行插入表中。