PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Fonction pour renvoyer un ensemble dynamique de colonnes pour une table donnée

Solution pour le cas simple

Comme expliqué dans les réponses référencées ci-dessous, vous pouvez utiliser des types enregistrés (lignes) et ainsi déclarer implicitement le type de retour d'une fonction polymorphe :

CREATE OR REPLACE FUNCTION public.get_table(_tbl_type anyelement)
  RETURNS SETOF anyelement AS
$func$
BEGIN
   RETURN QUERY EXECUTE format('TABLE %s', pg_typeof(_tbl_type));
END
$func$ LANGUAGE plpgsql;

Appel :

SELECT * FROM public.get_table(NULL::public.users);  -- note the syntax!

Renvoie le tableau complet (avec toutes les colonnes utilisateur).

Attendez ! Comment ?

Explication détaillée dans cette réponse connexe, chapitre"Divers types de tableaux complets" :

  • Refactoriser une fonction PL/pgSQL pour renvoyer la sortie de diverses requêtes SELECT

TABLE foo est juste l'abréviation de SELECT * FROM foo :

  • Existe-t-il un raccourci pour SELECT * FROM ?

2 étapes pour un type de retour entièrement dynamique

Mais ce que vous essayez de faire est strictement impossible dans un simple Commande SQL.

Je veux transmettre schema_name et table_name comme paramètres pour fonctionner et obtenir la liste des enregistrements, selon column_visible champ dans public.fields tableau.

Il n'existe aucun moyen direct de renvoyer une sélection arbitraire de colonnes (type de retour inconnu au moment de l'appel) à partir d'une fonction - ou any Commande SQL. SQL exige de connaître le nombre, les noms et les types de colonnes résultantes au moment de l'appel. Plus d'informations dans le 2ème chapitre de cette réponse connexe :

  • Comment puis-je générer un CROSS JOIN pivoté où la définition de table résultante est inconnue ?

Il existe différentes solutions de contournement . Vous pouvez envelopper le résultat dans l'un des types de documents standard (json , jsonb , hstore , xml ).

Ou vous générez la requête avec un appel de fonction et exécutez le résultat avec le suivant :

CREATE OR REPLACE FUNCTION public.generate_get_table(_schema_name text, _table_name text)
  RETURNS text AS
$func$
   SELECT format('SELECT %s FROM %I.%I'
               , string_agg(quote_ident(column_name), ', ')
               , schema_name
               , table_name)
   FROM   fields
   WHERE  column_visible
   AND    schema_name = _schema_name 
   AND    table_name  = _table_name
   GROUP  BY schema_name, table_name
   ORDER  BY schema_name, table_name;
$func$  LANGUAGE sql;

Appel :

SELECT public.generate_get_table('public', 'users');

Cela crée une requête de la forme :

SELECT usr_id, usr FROM public.users;

Exécutez-le dans la 2ème étape. (Vous pouvez ajouter des numéros de colonne et ordonner les colonnes.)
Ou ajoutez \gexec dans psql pour exécuter la valeur de retour immédiatement. Voir :

Comment forcer l'évaluation d'une sous-requête avant de rejoindre/pousser vers un serveur étranger

Assurez-vous de vous défendre contre l'injection SQL :

  • INSERT avec le nom de la table dynamique dans la fonction de déclenchement
  • Définir les noms de table et de colonne comme arguments dans une fonction plpgsql ?
En passant

varchar(100) n'a pas beaucoup de sens pour les identifiants, qui sont limités à 63 caractères dans Postgres standard :

  • Nombre maximal de caractères dans les libellés (noms de table, colonnes, etc.)

Si vous comprenez comment le type d'identifiant d'objet regclass fonctionne, vous pouvez remplacer le schéma et le nom de la table par un seul regclass colonne.