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)
ouformat(%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