PostgreSQL 中的子查询魔法

Den*_*nys 4 postgresql subquery postgresql-9.4

我有一个疑问:

update product_product 
set (write_date, default_code) = (LOCALTIMESTAMP, 'update') 
where product_tmpl_id in (
    select distinct product_tmpl_id 
    from product_template 
    where type='import');
Run Code Online (Sandbox Code Playgroud)

而且需要7个小时才能完成。但是,当我执行子查询时:

select distinct product_tmpl_id 
from product_template 
where type='import';
Run Code Online (Sandbox Code Playgroud)

我收到错误:

列“product_tmpl_id”不存在

product_tmpl_id表中没有列product_template

这是我的错误。第一个查询应该是:

update product_product set (write_date,default_code) = (LOCALTIMESTAMP,'update') 
where product_tmpl_id in
 (select distinct id from product_template where type='import');
Run Code Online (Sandbox Code Playgroud)

它只需要几秒钟即可运行。

所以我的问题如下:

  • 为什么第一个查询没有失败?
  • 为什么要运行这么长时间?
  • 它到底做了什么?

结果select version();

PostgreSQL 9.4.3 on x86_64-unknown-linux-gnu, 
compiled by gcc (Debian 4.9.2-10) 4.9.2, 64-bit
Run Code Online (Sandbox Code Playgroud)

Mar*_*ith 6

为什么第一个查询没有失败?

因为这是有效的 SQL。

首先,考虑SELECT对不引用该表中任何列的表运行 a 是有效的。

SELECT DISTINCT 'foo'
FROM   product_template
WHERE  type = 'import'; 
Run Code Online (Sandbox Code Playgroud)

foo如果存在与该WHERE子句匹配的任何行,则上面将返回单行结果。

其次考虑在子查询中引用外部表中的列是有效的(这是相关子查询工作所必需的)。

如果列名未通过表引用限定,则将在内部范围内(如果可能)和外部范围(否则)进行解析。在你的情况下product_template没有这样的列,所以它被解析为属于product_product.

作为最佳实践,明确列所属的表。如果子查询如下编写,它将无法编译并警告您错误。

IN (SELECT DISTINCT pt.product_tmpl_id
    FROM   product_template pt
    WHERE  pt.type = 'import'); 
Run Code Online (Sandbox Code Playgroud)

它到底做了什么?

这取决于。如果FROM product_template WHERE type = 'import';返回零行那么你很幸运,这相当于

update product_product 
set (write_date, default_code) = (LOCALTIMESTAMP, 'update') 
where product_tmpl_id = null; /*Never true*/
Run Code Online (Sandbox Code Playgroud)

可能你并不幸运,它确实返回了至少一行。在这种情况下,您运行了以下等效项

update product_product 
set (write_date, default_code) = (LOCALTIMESTAMP, 'update') 
where product_tmpl_id = product_tmpl_id;
Run Code Online (Sandbox Code Playgroud)

这相当于

update product_product 
set (write_date, default_code) = (LOCALTIMESTAMP, 'update') 
where product_tmpl_id IS NOT NULL;
Run Code Online (Sandbox Code Playgroud)

为什么要运行这么长时间?

我想首先它更新了表中的所有行。

我也不确定 Postgres 中的执行计划会是什么样的。

在最坏的情况下,它可能会选择与wherefrom匹配的所有行product_template,传入相关参数,然后DISTINCT对外部product_product表中的每一行执行结果。