PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Eseguire una query dinamica a campi incrociati

Quello che chiedi è impossibile . SQL è un linguaggio rigorosamente tipizzato. Le funzioni PostgreSQL devono dichiarare un tipo restituito (RETURNS .. ) al momento della creazione .

Un modo limitato per aggirare questo problema è con le funzioni polimorfiche. Se puoi fornire il tipo restituito al momento della funzione chiama . Ma questo non è evidente dalla tua domanda.

  • Refactoring di una funzione PL/pgSQL per restituire l'output di varie query SELECT

puoi restituire un risultato completamente dinamico con record anonimi. Ma poi è necessario fornire un elenco di definizioni di colonna con ogni chiamata. E come fai a sapere delle colonne restituite? Cattura 22.

Esistono varie soluzioni alternative, a seconda di ciò di cui hai bisogno o con cui puoi lavorare. Poiché tutte le colonne di dati sembrano condividere lo stesso tipo di dati, suggerisco di restituire un array :text[] . Oppure potresti restituire un tipo di documento come hstore o json . Correlati:

  • Alternativa dinamica al pivot con CASE e GROUP BY

  • Converti dinamicamente le chiavi hstore in colonne per un insieme sconosciuto di chiavi

Ma potrebbe essere più semplice usare solo due chiamate:1:Lascia che Postgres costruisca la query. 2:esegui e recupera le righe restituite.

  • Selezione di più valori max() utilizzando una singola istruzione SQL

Non userei la funzione di Eric Minikel come presentata nella tua domanda per niente . Non è sicuro contro l'iniezione di SQL tramite identificatori malformi dannosi. Usa format() per creare stringhe di query a meno che tu non stia eseguendo una versione obsoleta precedente a Postgres 9.1.

Un'implementazione più breve e più pulita potrebbe assomigliare a questa:

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;

Stessa chiamata di funzione della versione originale. La funzione crosstab() è fornito dal modulo aggiuntivo tablefunc che deve essere installato. Nozioni di base:

  • Query a campi incrociati PostgreSQL

Questo gestisce i nomi di colonne e tabelle in modo sicuro. Nota l'uso dei tipi di identificatore oggetto regclass e regtype . Funziona anche con nomi qualificati per schema.

  • Nome tabella come parametro di funzione PostgreSQL

Tuttavia, non è completamente sicuro mentre passi una stringa da eseguire come espressione (_expr - cellc nella tua query originale). Questo tipo di input è intrinsecamente pericoloso contro SQL injection e non dovrebbe mai essere esposto al pubblico in generale.

  • Iniezione SQL nelle funzioni di Postgres rispetto alle query preparate

Esegue la scansione della tabella solo una volta per entrambi gli elenchi di categorie e dovrebbe essere un po' più veloce.

Non è ancora possibile restituire tipi di riga completamente dinamici poiché ciò non è assolutamente possibile.