您可以将其包装在子查询中,但是如果没有
OFFSET 0hack,就不能保证安全。在9.3中,使用
LATERAL。该问题是由于解析器有效地将宏扩展
*为列列表而引起的。
解决方法
在哪里:
SELECt (my_func(x)).* FROM some_table;
将计算函数中结果列的
my_func n时间
n,公式如下:
SELECt (mf).* FROM ( SELECt my_func(x) AS mf FROM some_table) sub;
通常不会,并且往往不会在运行时添加其他扫描。为了保证不会执行多次评估,您可以使用
OFFSET 0黑客或滥用
PostgreSQL的失败来跨
CTE边界进行优化:
SELECt (mf).* FROM ( SELECt my_func(x) AS mf FROM some_table OFFSET 0) sub;
或者:
WITH tmp(mf) AS ( SELECt my_func(x) FROM some_table)SELECt (mf).* FROM tmp;
在PostgreSQL 9.3中,您可以
LATERAL用来获得更合理的行为:
SELECt mf.*FROM some_tableLEFT JOIN LATERAL my_func(some_table.x) AS mf ON true;
LEFT JOIN LATERAL ... ON true即使函数调用不返回任何行,也保留与原始查询类似的所有行。
演示版
创建一个不可内联的函数作为演示:
CREATE OR REPLACe FUNCTION my_func(integer)RETURNS TABLE(a integer, b integer, c integer) AS $$BEGIN RAISE NOTICE 'my_func(%)',$1; RETURN QUERY SELECT $1, $1, $1;END;$$ LANGUAGE plpgsql;
和伪数据表:
CREATE TABLE some_table AS SELECT x FROM generate_series(1,10) x;
然后尝试以上版本。您会看到第一个每次调用会引发三个通知;后者只举一个。
为什么?
好问题。这太糟糕了。
看起来像:
(func(x)).*
扩展为:
(my_func(x)).i, (func(x)).j, (func(x)).k, (func(x)).l
在解析,根据一看
debug_print_parse,debug_print_rewritten和debug_print_plan。(修剪后的)分析树如下所示:
:targetList ( {TARGETENTRY :expr {FIELDSELECT :arg {FUNCEXPR :funcid 57168 ... } :fieldnum 1 :resulttype 23 :resulttypmod -1 :resultcollid 0 } :resno 1 :resname i ... } {TARGETENTRY :expr {FIELDSELECT :arg {FUNCEXPR :funcid 57168 ... } :fieldnum 2 :resulttype 20 :resulttypmod -1 :resultcollid 0 } :resno 2 :resname j ... } {TARGETENTRY :expr {FIELDSELECT :arg {FUNCEXPR :funcid 57168 ... } :fieldnum 3 :... } :resno 3 :resname k ... } {TARGETENTRY :expr {FIELDSELECT :arg {FUNCEXPR :funcid 57168 ... } :fieldnum 4... } :resno 4 :resname l ... } )因此,基本上,我们使用的是哑巴分析器黑客工具,通过克隆节点来扩展通配符。



