我有一个表父表
:
create table parent
(
identifier serial primary key,
name text
);
现在我有以下两个功能:
create or replace function test_a() returns integer
as $$
insert into parent(name) values('testing') returning identifier;
$$ language sql;
create or replace function test_b() returns setof parent
as $$
delete from parent where identifier = (test_a()) returning parent.*;
$$ language sql;
现在我做到了:
select * from test_b();
这不会返回任何行。这是有道理的,delete
无法看到 insert
插入的行,因为该行刚刚插入,因此不在 select * 时定义的快照中from test_b()
查询被执行。
按照此逻辑,该行应该位于表中。那么让我们检查一下parent
:
select * from parent;
这不会返回任何行。我不明白这一点。为什么是这样?如果我的推理有缺陷(你能解释一下原因吗),为什么 select * from test_b()
然后不返回该行?这似乎是一个终极矛盾。
更新
如果我有以下两个函数,那就完全不同了:
create or replace function test_a() returns integer
as $$
insert into parent(name) values('testing') returning identifier;
$$ language sql;
create or replace function test_b() returns setof parent
as $$
delete from parent where identifier = (select test_a()) returning parent.*;
$$ language sql;
请注意,(select test_a())
是此处唯一的区别。
现在 select * from test_b()
仍然不返回行,但后续的 select * fromparent
返回我们插入的一行。根据我对上述快照范围的推理,这是有道理的。
= (test_a())
和 = (select test_a())
之间有区别吗?
最佳答案
我犹豫是否将此作为答案发布,因为我真的不知道你问题的答案,而且我相信其他人能够准确解释为什么会这样。但我发现它非常有趣,所以我玩了一下,这就是我的发现。
解释 SELECT * FROM 父 WHERE 标识符 = test_a();
父级上的顺序扫描(成本=0.00..26.20行=1宽度=12)
过滤器:(标识符= test_a())
对比
解释 SELECT * FROM 父 WHERE 标识符 = (SELECT test_a());
父级上的顺序扫描(成本=0.26..2.46行=1宽度=12)
过滤器:(标识符 = $0)
InitPlan 1(返回 $0)
->结果(成本=0.00..0.26行=1宽度=0)
(我放弃了PK)
(SELECT test_a())
是一个子查询。它预先解析一次,其结果用于其他记录的后续顺序扫描。
当您说identifier = test_a()
时,因为test_a()函数是 volatile 的,所以它将针对顺序扫描中读取的每个记录执行。 (如果函数是不可变的,则对 test_1a() 的调用将替换为不可变函数的值)。
现在试试这个:
- 截断父级
- 运行
SELECT * FROM test_b()
几次 SELECT * FROM Parent
- 您会发现没有创建任何内容。SELECT * FROM test_a()
- 仅创建单个记录- 运行
SELECT * FROM test_b()
几次 SELECT * FROM Parent
- 您现在不仅应该看到每次在第 5 步中运行查询时都有记录,而且还有更多记录。
我的解释:当你在没有数据的情况下运行 test_b() 时,永远不会到达调用 test_a() 的点 - 因为它没有提前解决,而是在对每条记录进行测试时解决。由于没有记录,因此永远不会调用 test_a()。现在您创建一条记录,然后再次调用 test_b()。这次将单个记录与调用 test_a() 的结果进行比较。再次运行它,两条记录都会与调用 test_a() 的结果进行比较(因此它被调用两次,创建另外两条记录)。多运行几次,表中的记录数每次都会增加一倍。
如前所述,(SELECT test_a())
版本由于使用子查询,因此会提前解析值。因此,当您调用 test_b() 时,它首先调用 test_a(),创建一条记录,即使 parent
中还没有可供读取的现有记录。因此,该版本可以在空表中创建记录,而其他版本如果不存在则不会创建任何记录。
关于postgresql - `= function()` 和 `= (select function())` 之间有区别吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48154879/