Możesz umieścić to w podzapytaniu, ale nie gwarantuje to bezpieczeństwa bez OFFSET 0
włamać się. W 9.3 użyj LATERAL
. Problem jest spowodowany tym, że parser skutecznie rozszerza makro *
do listy kolumn.
Obejście
Gdzie:
SELECT (my_func(x)).* FROM some_table;
oceni my_func
n
razy dla n
kolumny wynikowe z funkcji, to sformułowanie:
SELECT (mf).* FROM (
SELECT my_func(x) AS mf FROM some_table
) sub;
generalnie nie będzie i zwykle nie dodaje dodatkowego skanowania w czasie wykonywania. Aby zagwarantować, że nie zostanie przeprowadzona wielokrotna ocena, możesz użyć OFFSET 0
zhakować lub nadużyć niepowodzenia PostgreSQL w zakresie optymalizacji ponad granicami CTE:
SELECT (mf).* FROM (
SELECT my_func(x) AS mf FROM some_table OFFSET 0
) sub;
lub:
WITH tmp(mf) AS (
SELECT my_func(x) FROM some_table
)
SELECT (mf).* FROM tmp;
W PostgreSQL 9.3 możesz użyć LATERAL
aby uzyskać rozsądniejsze zachowanie:
SELECT mf.*
FROM some_table
LEFT JOIN LATERAL my_func(some_table.x) AS mf ON true;
LEFT JOIN LATERAL ... ON true
zachowuje wszystkie wiersze, tak jak oryginalne zapytanie, nawet jeśli wywołanie funkcji nie zwraca żadnego wiersza.
Demo
Utwórz funkcję, która nie jest inlineable jako demonstracja:
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;
oraz tabelę fikcyjnych danych:
CREATE TABLE some_table AS SELECT x FROM generate_series(1,10) x;
następnie wypróbuj powyższe wersje. Zobaczysz, że pierwsze powoduje trzy powiadomienia na wywołanie; ci ostatni podbijają tylko jeden.
Dlaczego?
Dobre pytanie. To okropne.
Wygląda tak:
(func(x)).*
jest rozwinięty jako:
(my_func(x)).i, (func(x)).j, (func(x)).k, (func(x)).l
w parsowaniu, zgodnie ze spojrzeniem na debug_print_parse
, debug_print_rewritten
i debug_print_plan
. (Przycięte) drzewo analizy wygląda tak:
: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
...
}
)
Więc w zasadzie używamy głupiego hacka parsera, aby rozszerzyć symbole wieloznaczne przez klonowanie węzłów.