Wie vermeide ich mehrfache Funktionsauswertungen mit der (func ()). * - Syntax in einer SQL-Abfrage?

Kontext

Wenn eine Funktion a zurückgibtTABLE oder einSETOF composite-type, wie diese Beispielfunktion:

CREATE FUNCTION func(n int) returns table(i int, j bigint) as $$
BEGIN
  RETURN QUERY select 1,n::bigint 
      union all select 2,n*n::bigint
      union all select 3,n*n*n::bigint;
END
$$ language plpgsql;

Auf die Ergebnisse kann mit verschiedenen Methoden zugegriffen werden:

1)select * from func(3) erzeugt diese Ausgabespalten:

 i | j 
---+---
 1 |  3
 2 |  9
 3 | 27

2)select func(3) Erzeugt nur eine Ausgabespalte vom Typ ROW.

 func  
-------
 (1,3)
 (2,9)
 (3,27)

3)select (func(3)).* wird wie # 1 produzieren:

 i | j 
---+---
 1 |  3
 2 |  9
 3 | 27

Wenn das Funktionsargument aus einer Tabelle oder einer Unterabfrage stammt, ist die Syntax 3 die einzig mögliche, wie in:

select N, (func(N)).* from (select 2 as N union select 3 as N) s;

oder wie in diesem verwandtenAntworten. Wenn wir hättenLATERAL JOIN wir könnten das benutzen, aber bis PostgreSQL 9.3 herauskommt, wird es nicht unterstützt und die vorherigen Versionen werden trotzdem jahrelang verwendet.

Problem

Das Problem mit der Syntax # 3 besteht nun darin, dass die Funktion so oft aufgerufen wird, wie das Ergebnis Spalten enthält. Es gibt keinen offensichtlichen Grund dafür, aber es passiert. Wir können es in Version 9.2 sehen, indem wir a hinzufügenRAISE NOTICE 'called for %', n in der Funktion. Mit der obigen Abfrage wird Folgendes ausgegeben:

NOTICE:  called for 2
NOTICE:  called for 2
NOTICE:  called for 3
NOTICE:  called for 3

Wenn nun die Funktion geändert wird, um 4 Spalten zurückzugeben, wie folgt:

CREATE FUNCTION func(n int) returns table(i int, j bigint,k int, l int) as $$
BEGIN
  raise notice 'called for %', n;
  RETURN QUERY select 1,n::bigint,1,1 
      union all select 2,n*n::bigint,1,1
      union all select 3,n*n*n::bigint,1,1;
END                                        
$$ language plpgsql stable;

dann wird die gleiche Abfrage ausgegeben:

NOTICE:  called for 2
NOTICE:  called for 2
NOTICE:  called for 2
NOTICE:  called for 2
NOTICE:  called for 3
NOTICE:  called for 3
NOTICE:  called for 3
NOTICE:  called for 3

Es wurden 2 Funktionsaufrufe benötigt, 8 wurden tatsächlich durchgeführt. Das Verhältnis ist die Anzahl der Ausgabespalten.

Mit der Syntax 2, die mit Ausnahme des Layouts der Ausgabespalten dasselbe Ergebnis liefert, treten diese mehreren Aufrufe nicht auf:

select N,func(N) from (select 2 as N union select 3 as N) s;

gibt:

NOTICE:  called for 2
NOTICE:  called for 3

gefolgt von den 6 resultierenden Zeilen:

 n |    func    
---+------------
 2 | (1,2,1,1)
 2 | (2,4,1,1)
 2 | (3,8,1,1)
 3 | (1,3,1,1)
 3 | (2,9,1,1)
 3 | (3,27,1,1)
Fragen

Gibt es eine Syntax oder ein Konstrukt mit 9.2, das das erwartete Ergebnis erzielt, wenn nur die minimal erforderlichen Funktionsaufrufe ausgeführt werden?

Bonusfrage: Warum finden die Mehrfachbewertungen überhaupt statt?