sql >> Base de Datos >  >> RDS >> PostgreSQL

Ejecutar una consulta de tabulación cruzada dinámica

Lo que pides es imposible . SQL es un lenguaje estrictamente tipado. Las funciones de PostgreSQL necesitan declarar un tipo de retorno (RETURNS .. ) en el momento de creación .

Una forma limitada de evitar esto es con funciones polimórficas. Si puede proporcionar el tipo de devolución en el momento de la llamada de la función . Pero eso no es evidente a partir de su pregunta.

  • Refactorice una función PL/pgSQL para devolver el resultado de varias consultas SELECT

Tu puedes devuelve un resultado completamente dinámico con registros anónimos. Pero luego debe proporcionar una lista de definición de columna con cada llamada. ¿Y cómo sabes acerca de las columnas devueltas? Captura 22.

Existen varias soluciones alternativas, según lo que necesite o con lo que pueda trabajar. Dado que todas sus columnas de datos parecen compartir el mismo tipo de datos, sugiero devolver una matriz :text[] . O podría devolver un tipo de documento como hstore o json . Relacionado:

  • Alternativa dinámica al pivote con CASE y GROUP BY

  • Convierta dinámicamente las claves de hstore en columnas para un conjunto desconocido de claves

Pero podría ser más simple usar solo dos llamadas:1:Deje que Postgres construya la consulta. 2:Ejecutar y recuperar filas devueltas.

  • Seleccionar múltiples valores max() usando una sola instrucción SQL

No usaría la función de Eric Minikel como se presenta en su pregunta en absoluto . No es seguro contra la inyección de SQL por medio de identificadores con formato incorrecto malicioso. Usar format() para crear cadenas de consulta a menos que esté ejecutando una versión obsoleta anterior a Postgres 9.1.

Una implementación más corta y limpia podría verse así:

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;

Misma llamada de función que su versión original. La función crosstab() es proporcionado por el módulo adicional tablefunc que tiene que ser instalado. Conceptos básicos:

  • Consulta de tabulación cruzada de PostgreSQL

Esto maneja los nombres de columnas y tablas de forma segura. Tenga en cuenta el uso de tipos de identificadores de objetos regclass y regtype . También funciona para nombres calificados de esquema.

  • Nombre de tabla como parámetro de función de PostgreSQL

Sin embargo, no es completamente seguro mientras pasa una cadena para que se ejecute como expresión (_expr - cellc en su consulta original). Este tipo de entrada es intrínsecamente insegura contra la inyección SQL y nunca debe exponerse al público en general.

  • Inyección de SQL en funciones de Postgres vs consultas preparadas

Escanea la tabla solo una vez para ambas listas de categorías y debería ser un poco más rápido.

Todavía no puedo devolver tipos de fila completamente dinámicos, ya que eso no es estrictamente posible.