O que você pede é impossível . SQL é uma linguagem estritamente tipada. As funções do PostgreSQL precisam declarar um tipo de retorno (
RETURNS ..
) no momento da criação . Uma maneira limitada de contornar isso é com funções polimórficas. Se você puder fornecer o tipo de retorno no momento da função chamar . Mas isso não é evidente na sua pergunta.
- Refatorar uma função PL/pgSQL para retornar a saída de várias consultas SELECT
Você pode retornar um resultado completamente dinâmico com registros anônimos. Mas então você é obrigado a fornecer uma lista de definição de coluna com cada chamada. E como você sabe sobre as colunas retornadas? Pegue 22.
Existem várias soluções alternativas, dependendo do que você precisa ou pode trabalhar. Como todas as suas colunas de dados parecem compartilhar o mesmo tipo de dados, sugiro retornar uma matriz :
text[]
. Ou você pode retornar um tipo de documento como hstore
ou json
. Relacionado:-
Alternativa dinâmica para pivotar com CASE e GROUP BY
-
Converta dinamicamente chaves hstore em colunas para um conjunto desconhecido de chaves
Mas pode ser mais simples usar apenas duas chamadas:1:Deixe o Postgres construir a consulta. 2:Execute e recupere as linhas retornadas.
- Selecionar vários valores max() usando uma única instrução SQL
Eu não usaria a função de Eric Minikel como apresentada em sua pergunta de forma alguma . Não é seguro contra injeção de SQL por meio de identificadores malformados mal-intencionados. Use
format()
para construir strings de consulta, a menos que você esteja executando uma versão desatualizada anterior ao Postgres 9.1. Uma implementação mais curta e mais limpa poderia ser assim:
CREATE OR REPLACE FUNCTION xtab(_tbl regclass, _row text, _cat text
, _expr text -- still vulnerable to SQL injection!
, _type regtype)
RETURNS text AS
$func$
DECLARE
_cat_list text;
_col_list text;
BEGIN
-- generate categories for xtab param and col definition list
EXECUTE format(
$$SELECT string_agg(quote_literal(x.cat), '), (')
, string_agg(quote_ident (x.cat), %L)
FROM (SELECT DISTINCT %I AS cat FROM %s ORDER BY 1) x$$
, ' ' || _type || ', ', _cat, _tbl)
INTO _cat_list, _col_list;
-- generate query string
RETURN format(
'SELECT * FROM crosstab(
$q$SELECT %I, %I, %s
FROM %I
GROUP BY 1, 2 -- only works if the 3rd column is an aggregate expression
ORDER BY 1, 2$q$
, $c$VALUES (%5$s)$c$
) ct(%1$I text, %6$s %7$s)'
, _row, _cat, _expr -- expr must be an aggregate expression!
, _tbl, _cat_list, _col_list, _type
);
END
$func$ LANGUAGE plpgsql;
Mesma chamada de função que sua versão original. A função
crosstab()
é fornecido pelo módulo adicional tablefunc
que deve ser instalado. Fundamentos:- Consulta de tabela cruzada PostgreSQL
Isso lida com nomes de colunas e tabelas com segurança. Observe o uso de tipos de identificador de objeto
regclass
e regtype
. Também funciona para nomes qualificados pelo esquema. - Nome da tabela como parâmetro de função do PostgreSQL
No entanto, não é totalmente seguro enquanto você passa uma string para ser executada como expressão (
_expr
- cellc
em sua consulta original). Esse tipo de entrada é inerentemente inseguro contra injeção de SQL e nunca deve ser exposto ao público em geral. - Injeção de SQL em funções do Postgres versus consultas preparadas
Verifica a tabela apenas uma vez para ambas as listas de categorias e deve ser um pouco mais rápido.
Ainda não é possível retornar tipos de linha completamente dinâmicos, pois isso não é estritamente possível.