ПРИСОЕДИНЯЙТЕСЬ к множеству возвращающих результатов функции
Я пытаюсь объединить таблицу и функцию, которая возвращает строки:
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;
Тесно связанный ответ: