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

Nom de table en tant que paramètre de fonction PostgreSQL

Cela peut être encore simplifié et amélioré :

CREATE OR REPLACE FUNCTION some_f(_tbl regclass, OUT result integer)
    LANGUAGE plpgsql AS
$func$
BEGIN
   EXECUTE format('SELECT (EXISTS (SELECT FROM %s WHERE id = 1))::int', _tbl)
   INTO result;
END
$func$;

Appel avec un nom qualifié par le schéma (voir ci-dessous) :

SELECT some_f('myschema.mytable');  -- would fail with quote_ident()

Ou :

SELECT some_f('"my very uncommon table name"');

Points majeurs

Utiliser un OUT paramètre pour simplifier la fonction. Vous pouvez directement sélectionner le résultat du SQL dynamique et le faire. Pas besoin de variables et de code supplémentaires.

EXISTS fait exactement ce que vous voulez. Vous obtenez true si la ligne existe ou false autrement. Il existe différentes façons de le faire, EXISTS est généralement le plus efficace.

Vous semblez vouloir un entier retour, donc j'ai jeté le boolean résultat de EXISTS en integer , ce qui donne exactement ce que vous aviez. Je retournerais booléen à la place.

J'utilise le type d'identifiant d'objet regclass comme type d'entrée pour _tbl . Qui fait tout quote_ident(_tbl) ou format('%I', _tbl) ferait l'affaire, mais en mieux, car :

  • .. il empêche l'injection SQL tout aussi bien.

  • .. il échoue immédiatement et plus gracieusement si le nom de la table est invalide / n'existe pas / est invisible pour l'utilisateur actuel. (Une regclass le paramètre n'est applicable que pour existant tableaux.)

  • .. cela fonctionne avec des noms de table qualifiés de schéma, où un simple quote_ident(_tbl) ou format(%I) échoueraient parce qu'ils ne peuvent pas résoudre l'ambiguïté. Vous devrez transmettre et échapper les noms de schéma et de table séparément.

Cela ne fonctionne que pour existant tables, évidemment.

J'utilise toujours format() , car cela simplifie la syntaxe (et pour montrer comment elle est utilisée), mais avec %s au lieu de %I . Généralement, les requêtes sont plus complexes donc format() aide plus. Pour l'exemple simple, nous pourrions tout aussi bien concaténer :

EXECUTE 'SELECT (EXISTS (SELECT FROM ' || _tbl || ' WHERE id = 1))::int'

Pas besoin de qualifier de table l'id colonne alors qu'il n'y a qu'une seule table dans le FROM liste. Aucune ambiguïté possible dans cet exemple. Commandes SQL (dynamiques) dans EXECUTE avoir une étendue distincte , les variables de fonction ou les paramètres n'y sont pas visibles - contrairement aux commandes SQL simples dans le corps de la fonction.

Voici pourquoi vous toujours échapper correctement l'entrée utilisateur pour le SQL dynamique :

db<>jouez ici démonstration de l'injection SQL
Ancien sqlfiddle