ПРИСОЕДИНЯЙТЕСЬ к множеству возвращающих результатов функции

Я пытаюсь объединить таблицу и функцию, которая возвращает строки:

SELECT p.id, p.name, f.action, f.amount
FROM person p
JOIN calculate_payments(p.id) f(id, action, amount) ON (f.id = p.id);

Эта функция возвращает 0, 1 или более строк для каждого идентификатора. Запрос работает на PostgreSQL 9.3, но на 9.1 он показывает следующую ошибку:

ERROR:  invalid reference to FROM-clause entry for table "p"
HINT:  There is an entry for table "p", but it cannot be referenced from this part of the query

Я не могу переместить вычисления из функции в запрос.
Я не могу использовать JOIN LATERAL, который, как я понимаю, является новой функцией в 9.3.
Есть ли решение этой проблемы?

1 ответ

Решение

В Postgres 9.1:

SELECT name, (f).*  -- note the parentheses!
FROM  (SELECT name, calculate_payments(id) AS f FROM person) sub;

Предполагая, что ваша функция имеет четко определенный тип возвращаемого значения с именами столбцов (id, action, amount) - информация отсутствует в вопросе.
Также предполагается, что ваша функция всегда возвращает одно и то же id он подается (что в данном случае является избыточным и может быть оптимизировано).

То же самое в гораздо более подробной форме:

SELECT sub.id, sub.name, (sub.f).action, (sub.f).amount  -- parentheses!
FROM  (
   SELECT p.id, p.name, calculate_payments(p.id) AS f(id, action, amount)
   FROM   person p
) sub;

Набор возвращающих функций в SELECT список результатов в несколько строк. Но это нестандартная и несколько странная особенность. Новый LATERAL особенность в pg 9.3+ является предпочтительной.

Вы можете разложить тип строки на том же шаге:

SELECT *, (calculate_payments(p.id)).*  -- parentheses!
FROM   person p

Но из-за слабости планировщика запросов Postgres это приводит к оценке функции один раз для каждого столбца:

Или в вашем случае:

SELECT p.id, p.name
     , (calculate_payments(p.id)).action
     , (calculate_payments(p.id)).amount
FROM   person p

Та же проблема: множественная оценка.

Чтобы быть точным, эквивалент решения в pg 9.3+:

SELECT p.id, p.name, f.action, f.amount
FROM   person p
LEFT   JOIN LATERAL calculate_payments(p.id) f ON TRUE;

Сохранение строк в результате, где функция возвращает 0 строк.

Если вас это не волнует, вы можете упростить в pg 9.3+:

SELECT p.id, p.name, f.action, f.amount
FROM   person p, calculate_payments(p.id) f;

Тесно связанный ответ:

Другие вопросы по тегам