Postgres 会执行在视图中未选择的计算列的计算吗?

Var*_*ath 8 postgresql view

我试图了解从视图中选择数据的性能影响,其中视图中的一列是原始表中其他数据的函数。

无论计算列是否在所选列的列表中,是否都会执行计算?

如果我有一张桌子并且视图像这样声明

CREATE TABLE price_data (
    ticker     text,          -- Ticker of the stock
    ddate      date,          -- Date for this price
    price      float8,        -- Closing price on this date
    factor     float8         -- Factor to convert this price to USD
);

CREATE VIEW prices AS 
    SELECT ticker, 
           ddate,
           price,
           factor,
           price * factor as price_usd
    FROM price_data
Run Code Online (Sandbox Code Playgroud)

乘法类似下面的查询来执行?

select ticker, ddate, price, factor from prices
Run Code Online (Sandbox Code Playgroud)

是否有参考以一种方式或另一种方式保证这一点?我正在阅读有关 Postgres 规则系统的文档,但我认为答案确实在于优化器,因为规则系统文档中没有任何内容表明它不会被选中。

我怀疑在上述情况下没有执行计算。我将视图更改为使用除法而不是乘法,并将0for插入factorprice_data. 上面的查询没有失败,但如果修改查询以选择计算列,则修改后的查询失败。

有什么方法可以理解在执行 a 时select正在执行哪些计算?我想我正在寻找类似EXPLAIN但也告诉我正在执行的计算的东西。

Nic*_*nes 6

正如@Laurenz 所说,您的分析是正确的:优化器将避免评估不影响查询结果的列表达式(并且您试图强制除以零错误证明了这一点)。

这取决于您选择的列,但也取决于列表达式的波动率类别。如果从不使用它们的输出,优化器可以自由地省略immutablestable函数调用,因为它们不会影响结果,但volatile函数可能有副作用,所以它们不容易被优化掉。

例如:

create function stable_function() returns int as $$
begin
  raise notice 'stable_function() called';
  return 1;
end
$$
language plpgsql stable;

create function volatile_function() returns int as $$
begin
  raise notice 'volatile_function() called';
  return 1;
end
$$
language plpgsql volatile;

create view v as
  select stable_function(), volatile_function();
Run Code Online (Sandbox Code Playgroud)

如果仅volatile选择列:

test=# explain (analyse, verbose) select volatile_function from v;
NOTICE:  volatile_function() called
                                           QUERY PLAN
------------------------------------------------------------------------------------------------
 Subquery Scan on v  (cost=0.00..0.27 rows=1 width=4) (actual time=0.057..0.057 rows=1 loops=1)
   Output: v.volatile_function
   ->  Result  (cost=0.00..0.26 rows=1 width=8) (actual time=0.056..0.056 rows=1 loops=1)
         Output: NULL::integer, volatile_function()
Run Code Online (Sandbox Code Playgroud)

...然后如您所见,输出中stable_function()不存在explain,并且缺少 aNOTICE确认此调用已被优化掉。

但是,如果stable改为选择该列:

test=# explain (analyse, verbose) select stable_function from v;
NOTICE:  stable_function() called
NOTICE:  volatile_function() called
                                           QUERY PLAN
------------------------------------------------------------------------------------------------
 Subquery Scan on v  (cost=0.00..0.52 rows=1 width=4) (actual time=0.139..0.139 rows=1 loops=1)
   Output: v.stable_function
   ->  Result  (cost=0.00..0.51 rows=1 width=8) (actual time=0.138..0.138 rows=1 loops=1)
         Output: stable_function(), volatile_function()
Run Code Online (Sandbox Code Playgroud)

...然后我们看到计划中出现了两个列表达式,并且NOTICEs 表明两个函数都已执行。

文档中似乎没有明确提及此行为,因此对于是否计算表达式没有硬性保证,并且您不应该依赖函数调用可能具有的任何副作用。

但是,如果您唯一关心的是性能,那么只要您将函数标记为stableimmutable在适当的地方,您就可以合理地确定(尤其是在这样的简单情况下)除非需要它们,否则不会对其进行评估。

(当您在那里审核您的波动率声明时,您可能还想设置并行安全标志。)