(func()).*
Using the (func()).*
syntax in PostgreSQL to access results from a function returning a composite type or TABLE
can lead to performance issues due to repeated function evaluations. This occurs because the syntax expands into multiple column references, each triggering a separate function call. The number of calls equals the number of columns in the result set.
The Problem: Inefficient Multiple Function Calls
The (func()).*
approach is inefficient because it doesn't optimize for minimal function calls. Ideally, the function should be called only once to retrieve the entire result set.
Solutions:
PostgreSQL 9.3 and later offer a straightforward solution using LATERAL
joins:
<code class="language-sql">SELECT mf.* FROM some_table LEFT JOIN LATERAL my_func(some_table.x) AS mf ON true;</code>
LATERAL
ensures that my_func
is executed only once per row in some_table
, significantly improving performance.
For older PostgreSQL versions (prior to 9.3), workarounds are necessary:
Workaround 1: OFFSET 0
Hack
This method leverages the OFFSET 0
clause to force a single function evaluation:
<code class="language-sql">SELECT (mf).* FROM ( SELECT my_func(x) AS mf FROM some_table OFFSET 0 ) sub;</code>
Workaround 2: Common Table Expression (CTE)
A CTE provides a cleaner alternative:
<code class="language-sql">WITH tmp AS ( SELECT my_func(x) FROM some_table ) SELECT (mf).* FROM tmp;</code>
Both workarounds ensure that my_func
is called only once, retrieving the complete result set before accessing individual columns.
Root Cause: Parser Expansion
The multiple evaluations arise from how PostgreSQL's parser interprets (func()).*
. It translates this into separate column accesses, resulting in multiple function invocations. This limitation has been addressed in newer versions with the introduction of LATERAL
joins.
The above is the detailed content of How to Avoid Multiple Function Evaluations with PostgreSQL's `(func()).*` Syntax?. For more information, please follow other related articles on the PHP Chinese website!