我编写了一个函数,根据参数在表mytable
中创建一行,并返回创建的行的id。
不幸的是,如果我使用以下语句,SELECT
将不返回任何行,就好像该函数的事务与SELECT
的事务不同。
SELECT * FROM mytable WHERE id = createComplexRow(...);
我的理解是我为select和function运行了相同的事务,然后应该能够读取未提交的行。
我正在尝试使用postgres 9.6
任何线索如何让它正常工作?
答案 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中也是如此;您可能会发现阅读文档中的那部分内容很有启发性。)
实际上,你应该很高兴它以这种方式处理,否则你最终会得到一个无限循环,继续在表格中插入行。