postgresql中的select function()对function()的调用过多 [英] select function() in postgresql makes too much calls to function()

查看:688
本文介绍了postgresql中的select function()对function()的调用过多的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我们具有此功能:

create or replace function foo(a integer)
returns table (b integer, c integer)
language plpgsql
as $$
begin
    raise notice 'foo()';
    return query select a*2, a*4;
    return query select a*6, a*8;
    return query select a*10, a*12;
end;
$$;

募集通知'foo()'"部分将用于知道函数被调用了多少次.

The "raise notice 'foo()'" part will be used to know how many time the function is called.

如果我这样调用函数:

postgres=# SELECT i, foo(i) as bla FROM generate_series(1,3) as i;
NOTICE:  foo()
NOTICE:  foo()
NOTICE:  foo()
 i |   bla   
---+---------
 1 | (2,4)
 1 | (6,8)
 1 | (10,12)
 2 | (4,8)
 2 | (12,16)
 2 | (20,24)
 3 | (6,12)
 3 | (18,24)
 3 | (30,36)
(9 rows)

我们可以看到,正如预期的那样,foo()被调用了3次.

We can see that, as expected, foo() is called 3 times.

但是,如果我以这种方式调用函数(因此我实际上在不同的列中获取foo()结果):

But if i call the function this way (so i actually gets foo() result in different columns):

postgres=# SELECT i, (foo(i)).* FROM generate_series(1,3) as i;
NOTICE:  foo()
NOTICE:  foo()
NOTICE:  foo()
NOTICE:  foo()
NOTICE:  foo()
NOTICE:  foo()
 i | b  | c  
---+----+----
 1 |  2 |  4
 1 |  6 |  8
 1 | 10 | 12
 2 |  4 |  8
 2 | 12 | 16
 2 | 20 | 24
 3 |  6 | 12
 3 | 18 | 24
 3 | 30 | 36
(9 rows)

我们可以看到foo()被调用了6次.并且如果foo()返回3列,则它将被调用9次.显然,对每个i及其返回的每一列都调用foo().

We can see that foo() is called 6 times. And if foo() was returning 3 columns, it would have been called 9 times. It's pretty clear that foo() is called for every i and every column it returns.

我不明白为什么postgres在这里没有进行优化.这对我来说是个问题,因为我的(实际)foo()可能占用大量CPU.有什么主意吗?

I don't understand why postgres does not make an optimisation here. And this is a problem for me as my (real) foo() may be CPU intensive. Any idea ?

使用不可变"函数或不返回多行的函数会产生相同的行为:

Using an "immutable" function or a function that does not return multiple rows gives the same behaviour:

create or replace function foo(a integer)
returns table (b integer, c integer, d integer)
language plpgsql
immutable
as $$
begin
raise notice 'foo';
return query select a*2, a*3, a*4;
end;
$$;

postgres=# select i, (foo(i)).* from generate_series(1,2) as i;
NOTICE:  foo
NOTICE:  foo
NOTICE:  foo
NOTICE:  foo
NOTICE:  foo
NOTICE:  foo
 i | b | c | d 
---+---+---+---
 1 | 2 | 3 | 4
 2 | 4 | 6 | 8
(2 rows)

推荐答案

这是一个已知问题.

SELECT (f(x)).*

在解析时被宏扩展为

SELECT (f(x)).a, (f(x)).b, ...

并且PostgreSQL不会合并对同一函数的多次调用,直到一次调用.

and PostgreSQL doesn't coalesce multiple calls to the same function down to a single call.

为避免此问题,您可以将其包装在子查询的另一层中,以便在对函数结果(而不是函数调用)的简单引用上进行宏扩展:

To avoid the issue you can wrap it in another layer of subquery so that the macro-expansion occurs on a simple reference to the function's result rather than the function invocation:

select i, (f).* 
FROM (
    SELECT i, foo(i) f from generate_series(1,2) as i
) x(i, f)

或在FROM子句中使用横向调用,这是较新版本的首选:

or use a lateral call in the FROM clause, which is preferred for newer versions:

select i, f.*
from generate_series(1,2) as i
    CROSS JOIN LATERAL foo(i) f;

使用传统的逗号联接和隐式横向联接,可以省略CROSS JOIN LATERAL,但是我发现将其包括在内相当明显,尤其是在混合其他联接类型时.

The CROSS JOIN LATERAL may be omitted, using legacy comma joins and an implicit lateral join, but I find it considerably clear to include it, especially when you're mixing other join types.

这篇关于postgresql中的select function()对function()的调用过多的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆