Можете да го обвиете в подзаявка, но това не е гарантирано безопасно без 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;
след това опитайте горните версии. Ще видите, че първото повдига три известия на извикване; последните вдигат само един.
Защо?
Добър въпрос. Ужасно е.
Изглежда така:
(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
...
}
)
По принцип използваме тъп хак за анализатор, за да разширим заместващи знаци чрез клониране на възли.