sql >> Database >  >> RDS >> PostgreSQL

Tabelnaam als een PostgreSQL-functieparameter

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) of format(%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



  1. Waarschuwing:mysqli_connect():(HY000/2002):Geen dergelijk bestand of map

  2. Hoe een geneste tabel te maken met behulp van door de gebruiker gedefinieerd gegevenstype in Oracle Database

  3. Hoe schrijf je een CASE-instructie in SQL?

  4. De UPDATE-instructie was in strijd met de REFERENCE-beperking - SQL Server / TSQL-zelfstudie, deel 76