Je kunt het in een subquery verpakken, maar dat is niet gegarandeerd veilig zonder de OFFSET 0
hacken. Gebruik in 9.3 LATERAL
. Het probleem wordt veroorzaakt doordat de parser effectief macro-uitbreidt *
in een kolomlijst.
Tussenoplossing
Waar:
SELECT (my_func(x)).* FROM some_table;
zal my_func
evaluate evalueren n
tijden voor n
resultaatkolommen van de functie, deze formulering:
SELECT (mf).* FROM (
SELECT my_func(x) AS mf FROM some_table
) sub;
over het algemeen niet, en heeft de neiging om geen extra scan toe te voegen tijdens runtime. Om te garanderen dat meerdere evaluaties niet worden uitgevoerd, kunt u de OFFSET 0
. gebruiken hack of misbruik het falen van PostgreSQL om over CTE-grenzen heen te optimaliseren:
SELECT (mf).* FROM (
SELECT my_func(x) AS mf FROM some_table OFFSET 0
) sub;
of:
WITH tmp(mf) AS (
SELECT my_func(x) FROM some_table
)
SELECT (mf).* FROM tmp;
In PostgreSQL 9.3 kunt u LATERAL
. gebruiken om gezonder gedrag te krijgen:
SELECT mf.*
FROM some_table
LEFT JOIN LATERAL my_func(some_table.x) AS mf ON true;
LEFT JOIN LATERAL ... ON true
behoudt alle rijen zoals de originele query, zelfs als de functieaanroep geen rij retourneert.
Demo
Maak een functie die niet inlineable is als demonstratie:
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;
en een tabel met dummy-gegevens:
CREATE TABLE some_table AS SELECT x FROM generate_series(1,10) x;
probeer dan de bovenstaande versies. U zult zien dat de eerste drie kennisgevingen per aanroep oproept; de laatste verhoogt er maar één.
Waarom?
Goede vraag. Het is verschrikkelijk.
Het ziet eruit als:
(func(x)).*
wordt uitgevouwen als:
(my_func(x)).i, (func(x)).j, (func(x)).k, (func(x)).l
bij het ontleden, volgens een blik op debug_print_parse
, debug_print_rewritten
en debug_print_plan
. De (bijgesneden) ontledingsboom ziet er als volgt uit:
: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
...
}
)
Dus eigenlijk gebruiken we een domme parser-hack om wildcards uit te breiden door knooppunten te klonen.