Вызов функции set-returns с аргументом массива несколько раз

Это вариация функции plpgsql, которая возвращает несколько столбцов, которые вызывается несколько раз. Тем не менее, я надеялся найти решение своих конкретных обстоятельств.

У меня есть функция, которая обрабатывает массив строк с заданным параметром и возвращает набор строк + новый столбец.

CREATE OR REPLACE foo(data data[], parameter int) RETURNS SETOF enhanceddata AS
...

Функция работает в тестовом примере только с одним набором данных

SELECT * FROM foo( (SELECT ARRAY_AGG(data) FROM datatable GROUP BY dataid WHERE dataid = something), 1) 

Но я хотел бы заставить его работать с несколькими группами данных, не передавая функции dataid. Я попробовал несколько вариантов:

SELECT dataid, (foo(ARRAY_AGG(data)),1).*
FROM dataset
WHERE dataid = something -- only testing on 1
GROUP BY dataid

Но функция вызывается один раз для каждого столбца.

Ответ 1

В Postgres 9.3 или новее обычно лучше всего использовать LEFT JOIN LATERAL... ON true:

SELECT sub.dataid, f.*
FROM  (
   SELECT dataid, array_agg(data) AS arr
   FROM   dataset
   WHERE  dataid = something
   GROUP  BY 1
   ) sub
LEFT   JOIN LATERAL foo(sub.arr) f ON true;

Если функция foo() может вернуть ни одной строки, это безопасная форма, поскольку она сохраняет все строки слева от объединения, даже если справа не возвращена ни одна строка.

Иначе, или если вы хотите исключить строки без результата из бокового соединения, используйте:

CROSS JOIN LATERAL foo(sub.arr)

или стенография:

, foo(sub.arr)

В руководстве есть явное упоминание.

Ответ, связанный с Крейгом (на который ссылается Даниэль), обновляется соответственно

Ответ 2

Функция вызывается в этом контексте несколько раз не из-за ее входов, а из-за реализации func().*

Это объясняется: Как избежать множественных функций с помощью синтаксиса (func()). * в SQL-запросе?

Следующий вариант должен работать без множественных оценок во всех поддерживаемых версиях PostgreSQL (8.4 или новее):

WITH subq as (
  SELECT array_agg(data) as agg,
   dataid FROM datatable
   -- WHERE clause ?
   GROUP BY dataid)
SELECT foo(agg,dataid) FROM subq;