postgresql中的select function()对函数()调用过多
假设我们有这个函数:postgresql中的select function()对函数()调用过多,sql,postgresql,stored-procedures,plpgsql,Sql,Postgresql,Stored Procedures,Plpgsql,假设我们有这个函数: 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; e
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;
$$;
“raisenotice'foo()”部分将用于了解调用函数的次数
如果我这样调用函数:
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次
但是如果我以这种方式调用该函数(因此我实际上在不同的列中得到foo()结果):
我们可以看到foo()被调用了6次。如果foo()返回3列,它将被调用9次。很明显,foo()是为每个i和它返回的每个列调用的
我不明白为什么博士后不在这里进行优化。这对我来说是个问题,因为我的(真实的)foo()可能是CPU密集型的。有什么想法吗
编辑:
使用“不可变”函数或不返回多行的函数会产生相同的行为:
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
子句中不调用返回多个值的函数(尤其是返回集合的函数)是合理的。
事实上,postgres并没有对这样的调用进行任何优化。
将函数放在from
子句中
SELECT i, f.* FROM generate_series(1,3) as i, foo(i) f;
在中,您可以找到注释(强调我的):
目前,还可以在select中调用返回集合的函数
查询的列表。对于查询本身生成的每一行
调用函数返回集,并为其生成输出行
函数结果集的每个元素。然而,请注意,这
该功能已弃用,可能会在将来的版本中删除
基本上,在
select
子句中不调用返回多个值的函数(尤其是返回集合的函数)是合理的。
事实上,postgres并没有对这样的调用进行任何优化。
将函数放在from
子句中
SELECT i, f.* FROM generate_series(1,3) as i, foo(i) f;
在中,您可以找到注释(强调我的):
目前,还可以在select中调用返回集合的函数
查询的列表。对于查询本身生成的每一行
调用函数返回集,并为其生成输出行
函数结果集的每个元素。然而,请注意,这
该功能已弃用,可能会在将来的版本中删除
这是一个众所周知的问题
SELECT (f(x)).*
宏在解析时是否扩展为
SELECT (f(x)).a, (f(x)).b, ...
PostgreSQL不会将对同一函数的多个调用合并为一个调用
为了避免此问题,您可以将其包装在另一层子查询中,以便宏扩展发生在对函数结果的简单引用上,而不是函数调用上:
select i, (f).*
FROM (
SELECT i, foo(i) f from generate_series(1,2) as i
) x(i, f)
或者在FROM
子句中使用横向调用,这是较新版本的首选:
select i, f.*
from generate_series(1,2) as i
CROSS JOIN LATERAL foo(i) f;
使用传统的逗号连接和隐式的横向连接,交叉连接横向连接可以省略,但是我发现包含它非常明显,特别是当您混合其他连接类型时。这是一个已知的问题
SELECT (f(x)).*
宏在解析时是否扩展为
SELECT (f(x)).a, (f(x)).b, ...
PostgreSQL不会将对同一函数的多个调用合并为一个调用
为了避免此问题,您可以将其包装在另一层子查询中,以便宏扩展发生在对函数结果的简单引用上,而不是函数调用上:
select i, (f).*
FROM (
SELECT i, foo(i) f from generate_series(1,2) as i
) x(i, f)
或者在FROM
子句中使用横向调用,这是较新版本的首选:
select i, f.*
from generate_series(1,2) as i
CROSS JOIN LATERAL foo(i) f;
使用传统的逗号连接和隐式的横向连接,交叉连接横向连接
可能会被省略,但我发现包含它相当明显,特别是当您混合其他连接类型时。在select
子句中调用函数是完全合理的-但是调用返回集合的函数是不合理的(=多行)在select
条款中,该函数不返回集合,而是返回一行。在select
中调用集合返回函数时会出现不同的问题,LATERAL
仍然是首选。@klin Er.是的。我请求-enocafee。多个调用的问题不是由它返回集合来调用的,而是返回集合。这是tot调用select
子句中的函数是合理的,但调用返回集合(=多行)的函数是不合理的在select
clause中,该函数不返回集合,而是返回一行。在select
中调用集合返回函数时,会出现不同的问题,LATERAL
仍然是首选。@klin Er.是的。我请求-enocafee。多个调用的问题不是由它返回集合来调用的,而是返回集合。这是什么“x(i,f)”?它只是一个花哨的别名还是别的什么?如果我用一个随机字符串替换它,请求仍然有效。@user368507是的,它指定子查询返回的表和列。表名部分是必需的。如果省略,列默认为内部查询的列名。“x(i,f)”是什么“?它只是一个花哨的别名还是别的什么?如果我用随机字符串替换它,请求仍然有效。@user368507是的,它指定子查询返回的表和列。表名部分是必需的。如果省略,则默认为内部查询的列名。”。