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

标签 postgresql

我有一个表父表:

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() 的调用将替换为不可变函数的值)。

现在试试这个:

  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 步中运行查询时都有记录,而且还有更多记录。

我的解释:当你在没有数据的情况下运行 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/

相关文章:

sql - PostgreSQL 按年份限制结果

postgresql - Postgres - 授予所有权限,但仍然拒绝表

ruby-on-rails - heroku 上的 Rails : after push, 得到 "PG::UniqueViolation: ERROR: duplicate key value violates unique constraint"

ruby-on-rails - rails : FATAL: Password Authentication Failed For User

python - 数组值必须以 "{"或维度信息开头

postgresql - 如何将前缀匹配附加到 PostgreSQL 中的 tsquery

java - JBoss7 + PostgreSQL 新的缺失/未满足的依赖关系

sql - 为 WHERE 序列中的每个元素向 SQL 查询添加行

java - 在 postgresql 中以范围类型保存 Java 对象

sql - 如何左连接和删除两个表之间的公共(public)字段