Detta kan förenklas och förbättras ytterligare:
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$;
Ring med schemakvalificerat namn (se nedan):
SELECT some_f('myschema.mytable'); -- would fail with quote_ident()
Eller:
SELECT some_f('"my very uncommon table name"');
Huvudpunkter
Använd en OUT
parameter för att förenkla funktionen. Du kan direkt välja resultatet av den dynamiska SQL in i den och vara klar. Inget behov av ytterligare variabler och kod.
EXISTS
gör precis som du vill. Du får true
om raden finns eller false
annat. Det finns olika sätt att göra detta, EXISTS
är vanligtvis mest effektiv.
Du verkar vilja ha ett heltal tillbaka, så jag kastade boolean
resultat från EXISTS
till integer
, vilket ger exakt vad du hade. Jag skulle återvända boolesk istället.
Jag använder objektidentifieringstypen regclass
som indatatyp för _tbl
. Det gör allt quote_ident(_tbl)
eller format('%I', _tbl)
skulle göra, men bättre, eftersom:
-
.. det förhindrar SQL-injektion lika bra.
-
.. det misslyckas omedelbart och mer elegant om tabellnamnet är ogiltigt / inte finns / är osynligt för den aktuella användaren. (En
regclass
parametern är endast tillämplig för befintlig tabeller.) -
.. det fungerar med schemakvalificerade tabellnamn, där en vanlig
quote_ident(_tbl)
ellerformat(%I)
skulle misslyckas eftersom de inte kan lösa tvetydigheten. Du skulle behöva skicka och escape schema- och tabellnamn separat.
Det fungerar bara för befintliga tabeller, så klart.
Jag använder fortfarande format()
, eftersom det förenklar syntaxen (och för att visa hur det används), men med %s
istället för %I
. Vanligtvis är frågor mer komplexa så format()
hjälper mer. För det enkla exemplet skulle vi lika gärna kunna sammanfoga:
EXECUTE 'SELECT (EXISTS (SELECT FROM ' || _tbl || ' WHERE id = 1))::int'
Inget behov av att tabellkvalificera id
kolumnen medan det bara finns en enda tabell i FROM
lista. Ingen tvetydighet möjlig i detta exempel. (Dynamisk) SQL-kommandon inuti EXECUTE
har ett separat omfattning , funktionsvariabler eller parametrar är inte synliga där - till skillnad från vanliga SQL-kommandon i funktionskroppen.
Här är varför du alltid escape användarinmatning för dynamisk SQL korrekt:
db<>spela här demonstrerar SQL-injektion
Gammal sqlfiddle