Как избежать множественных ошибок функций с помощью синтаксиса (func ()). * В запросе SQL?
Когда функция возвращаетTABLE
илиSETOF composite-type
, как этот пример функции:
CREATE FUNCTION func(n int) returns table(i int, j bigint) as $
BEGIN
RETURN QUERY select 1,n::bigint
union all select 2,n*n::bigint
union all select 3,n*n*n::bigint;
END
$ language plpgsql;
К результатам можно получить доступ различными способами:
1)select * from func(3)
произведет эти выходные столбцы:
i | j ---+--- 1 | 3 2 | 9 3 | 27
2)select func(3)
будет производить только один выходной столбец типа ROW.
func ------- (1,3) (2,9) (3,27)
3)select (func(3)).*
будет производить как # 1:
i | j ---+--- 1 | 3 2 | 9 3 | 27
Когда аргумент функции происходит из таблицы или подзапроса, синтаксис # 3 является единственным возможным, как в:
select N, (func(N)).* from (select 2 as N union select 3 as N) s;
или как в этом связанномответ, Если бы мы имелиLATERAL JOIN
мы могли бы использовать это, но до выхода PostgreSQL 9.3 он не поддерживается, и предыдущие версии все равно будут использоваться годами.
Теперь проблема с синтаксисом # 3 заключается в том, что функция вызывается столько раз, сколько столбцов в результате. Там нет очевидной причины для этого, но это происходит. Мы можем увидеть это в версии 9.2, добавивRAISE NOTICE 'called for %', n
в функции. С запросом выше, это выводит:
NOTICE: called for 2 NOTICE: called for 2 NOTICE: called for 3 NOTICE: called for 3
Теперь, если функция изменена так, чтобы она возвращала 4 столбца, вот так:
CREATE FUNCTION func(n int) returns table(i int, j bigint,k int, l int) as $
BEGIN
raise notice 'called for %', n;
RETURN QUERY select 1,n::bigint,1,1
union all select 2,n*n::bigint,1,1
union all select 3,n*n*n::bigint,1,1;
END
$ language plpgsql stable;
затем тот же запрос выводит:
NOTICE: called for 2 NOTICE: called for 2 NOTICE: called for 2 NOTICE: called for 2 NOTICE: called for 3 NOTICE: called for 3 NOTICE: called for 3 NOTICE: called for 3
Потребовалось 2 вызова функций, фактически было сделано 8. Соотношение - это количество выходных столбцов.
С синтаксисом № 2, который дает тот же результат, за исключением разметки выходных столбцов, эти множественные вызовы не происходят
select N,func(N) from (select 2 as N union select 3 as N) s;
дает:
NOTICE: called for 2 NOTICE: called for 3
затем следуют 6 строк:
n | func ---+------------ 2 | (1,2,1,1) 2 | (2,4,1,1) 2 | (3,8,1,1) 3 | (1,3,1,1) 3 | (2,9,1,1) 3 | (3,27,1,1)Вопросов
Существует ли синтаксис или конструкция с 9.2, которая могла бы достичь ожидаемого результата, выполняя только минимальные требуемые вызовы функций?
Бонусный вопрос: почему множественные оценки вообще происходят?