Dit kan verder worden vereenvoudigd en verbeterd:
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$;
Bellen met schema-gekwalificeerde naam (zie hieronder):
SELECT some_f('myschema.mytable'); -- would fail with quote_ident()
Of:
SELECT some_f('"my very uncommon table name"');
Belangrijkste punten
Gebruik een OUT
parameter om de functie te vereenvoudigen. U kunt het resultaat van de dynamische SQL er direct in selecteren en klaar zijn. Geen extra variabelen en code nodig.
EXISTS
doet precies wat je wilt. Je krijgt true
als de rij bestaat of false
anders. Er zijn verschillende manieren om dit te doen, EXISTS
is doorgaans het meest efficiënt.
Je lijkt een geheel getal . te willen terug, dus ik cast de boolean
resultaat van EXISTS
naar integer
, die precies oplevert wat je had. Ik zou booleaans teruggeven in plaats daarvan.
Ik gebruik het type object-ID regclass
als invoertype voor _tbl
. Dat doet alles quote_ident(_tbl)
of format('%I', _tbl)
zou doen, maar beter, omdat:
-
.. het voorkomt SQL-injectie net zo goed.
-
.. het mislukt onmiddellijk en eleganter als de tabelnaam ongeldig is / niet bestaat / onzichtbaar is voor de huidige gebruiker. (Een
regclass
parameter is alleen van toepassing op bestaande tabellen.) -
.. het werkt met schema-gekwalificeerde tabelnamen, waarbij een eenvoudige
quote_ident(_tbl)
offormat(%I)
zou mislukken omdat ze de dubbelzinnigheid niet kunnen oplossen. Je zou schema- en tabelnamen afzonderlijk moeten doorgeven en escapen.
Het werkt alleen voor bestaande tafels natuurlijk.
Ik gebruik nog steeds format()
, omdat het de syntaxis vereenvoudigt (en om te demonstreren hoe het wordt gebruikt), maar met %s
in plaats van %I
. Doorgaans zijn zoekopdrachten complexer, dus format()
helpt meer. Voor het eenvoudige voorbeeld kunnen we net zo goed samenvoegen:
EXECUTE 'SELECT (EXISTS (SELECT FROM ' || _tbl || ' WHERE id = 1))::int'
Het is niet nodig om de id
aan een tabel te kwalificeren kolom terwijl er maar één tabel is in de FROM
lijst. Geen dubbelzinnigheid mogelijk in dit voorbeeld. (Dynamische) SQL-opdrachten binnen EXECUTE
hebben een apart bereik , functievariabelen of parameters zijn daar niet zichtbaar - in tegenstelling tot gewone SQL-opdrachten in de hoofdtekst van de functie.
Dit is waarom je altijd ontsnap gebruikersinvoer voor dynamische SQL correct:
db<>viool hier SQL-injectie demonstreren
Oude sqlfiddle