サブクエリでまとめることはできますが、OFFSET 0
がないと安全が保証されません。 ハック。 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_table
LEFT 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;
次に、上記のバージョンを試してください。最初の通知では、呼び出しごとに3つの通知が発生することがわかります。後者は1つだけ上げます。
なぜですか?
良い質問。ひどいです。
次のようになります:
(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
...
}
)
つまり、基本的には、ダムパーサーハックを使用して、ノードのクローンを作成することでワイルドカードを拡張しています。