`= function()`和`=(select function())`之间有区别吗?

时间:2018-01-08 17:07:43

标签: postgresql

我有一张表parent

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 * from parent返回我们插入的一行。根据我对上述快照范围的推理,这是有道理的。

= (test_a())= (select test_a())之间是否存在差异?

1 个答案:

答案 0 :(得分:2)

我毫不犹豫地将此作为答案发布,因为我不知道你的问题的答案,而且我确信别人能够解释为什么会这样。但我发现它非常有趣,所以我玩了一下,这就是我发现的。

EXPLAIN SELECT * FROM parent WHERE identifier = test_a();

Seq Scan on parent (cost=0.00..26.20 rows=1 width=12) Filter: (identifier = test_a())

VS

EXPLAIN SELECT * FROM parent WHERE identifier = (SELECT test_a());

Seq Scan on parent (cost=0.26..2.46 rows=1 width=12) Filter: (identifier = $0) InitPlan 1 (returns $0) -> Result (cost=0.00..0.26 rows=1 width=0)

(我已经放弃了PK)

(SELECT test_a())是子查询。它被预先解决一次,其结果用于随后的其他记录的顺序扫描。

当您说identifier = test_a()时,因为test_a()函数是易失性的,所以它将针对顺序扫描中读取的每个记录执行。 (如果函数是不可变的,则对test_1a()的调用将替换为不可变函数的值。)

现在试试这个:

  1. 截断父级
  2. 运行SELECT * FROM test_b()几次
  3. SELECT * FROM parent - 您发现没有创建任何内容。
  4. SELECT * FROM test_a() - 仅创建一条记录
  5. 运行SELECT * FROM test_b()几次
  6. SELECT * FROM parent - 您应该看到,您现在不仅每次在步骤5中运行查询时都有记录,而且还有更多记录。
  7. 我的解释:当你运行test_b()时没有数据,从来没有达到调用test_a()的点 - 因为它没有提前解决,而是在为每条记录进行测试时解决了。由于没有记录,因此永远不会调用test_a()。现在您创建一条记录,然后再次调用test_b()。这次将单个记录与对test_a()的调用结果进行比较。再次运行它,并将两个记录与test_a()调用的结果进行比较(因此它被调用两次,再创建两个记录)。再运行几次,每次表中的记录数量翻倍。

    如前所述,(SELECT test_a())版本由于使用子查询,因此会提前解析值。因此,当您调用test_b()时,它首先调用test_a(),创建一条记录,即使parent中还没有现有记录可供阅读。因此,该版本可以在空表中创建记录,而另一个版本不会创建任何记录(如果不存在)。