Ce que vous demandez est impossible . SQL est un langage strictement typé. Les fonctions PostgreSQL doivent déclarer un type de retour (RETURNS ..
) au moment de la création .
Un moyen limité de contourner ce problème consiste à utiliser des fonctions polymorphes. Si vous pouvez fournir le type de retour au moment de l'appel de la fonction . Mais cela ne ressort pas de votre question.
- Refactoriser une fonction PL/pgSQL pour renvoyer la sortie de diverses requêtes SELECT
Vous pouvez renvoie un résultat complètement dynamique avec des enregistrements anonymes. Mais vous devez alors fournir une liste de définitions de colonne avec chaque appel. Et comment connaissez-vous les colonnes renvoyées ? Attrape 22.
Il existe différentes solutions de contournement, en fonction de ce dont vous avez besoin ou avec lequel vous pouvez travailler. Étant donné que toutes vos colonnes de données semblent partager le même type de données, je suggère de renvoyer un tableau :text[]
. Ou vous pouvez renvoyer un type de document comme hstore
ou json
. Connexe :
-
Alternative dynamique au pivot avec CASE et GROUP BY
-
Convertir dynamiquement les clés hstore en colonnes pour un ensemble de clés inconnu
Mais il pourrait être plus simple d'utiliser simplement deux appels :1 :Laissez Postgres construire la requête. 2 :Exécuter et récupérer les lignes renvoyées.
- Sélectionner plusieurs valeurs max() à l'aide d'une seule instruction SQL
Je n'utiliserais pas du tout la fonction d'Eric Minikel telle que présentée dans votre question du tout . Il n'est pas à l'abri d'une injection SQL au moyen d'identifiants malformés de manière malveillante. Utilisez format()
pour créer des chaînes de requête, sauf si vous exécutez une version obsolète antérieure à Postgres 9.1.
Une mise en œuvre plus courte et plus propre pourrait ressembler à ceci :
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;
Même appel de fonction que votre version originale. La fonction crosstab()
est fourni par le module supplémentaire tablefunc
qui doit être installé. Notions de base :
- Requête croisée PostgreSQL
Cela gère les noms de colonne et de table en toute sécurité. Notez l'utilisation des types d'identifiant d'objet regclass
et regtype
. Fonctionne également pour les noms qualifiés de schéma.
- Nom de table en tant que paramètre de fonction PostgreSQL
Cependant, il n'est pas complètement sûr pendant que vous passez une chaîne à exécuter en tant qu'expression (_expr
- cellc
dans votre requête d'origine). Ce type d'entrée est intrinsèquement dangereux contre l'injection SQL et ne doit jamais être exposé au grand public.
- Injection SQL dans les fonctions Postgres vs requêtes préparées
N'analyse la table qu'une fois pour les deux listes de catégories et devrait être un peu plus rapide.
Impossible de renvoyer des types de lignes complètement dynamiques car ce n'est strictement pas possible.