PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Executar uma consulta de tabela cruzada dinâmica


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.