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.