sql >> Database >  >> RDS >> PostgreSQL

Tekstuitvoer van een functie gebruiken als nieuwe query

De truc met PREPARE werkt niet, omdat er geen * tekststring* (een waarde) voor nodig is, zoals CREATE FUNCTION doet, maar een geldige verklaring (code).

gegevens converteren in uitvoerbare code je moet dynamische SQL gebruiken, d.w.z. EXECUTE in een plpgsql-functie of DO uitspraak. Dit werkt probleemloos zolang het retourtype niet afhangt van de uitkomst van de eerste functie myresult() . Anders ben je terug om 22 te vangen, zoals uiteengezet in mijn vorige antwoord:

  • Hoe een stringresultaat van een opgeslagen procedure in postgres uit te voeren

Het cruciale onderdeel is om het retourtype te declareren (rijtype in dit geval) op de een of andere manier. U kunt een TABLE . maken , TEMP TABLE of TYPE Voor het doel. Of u kunt een voorbereide verklaring of een refcursor gebruiken.

Oplossing met voorbereide verklaring

Je bent heel dichtbij geweest. Het ontbrekende stukje van de puzzel is om de gegenereerde query voor te bereiden met dynamische SQL .

Functie om verklaring dynamisch voor te bereiden

Maak deze functie eenmaal . Het is een geoptimaliseerde en veilige versie van uw functie myresult() :

CREATE OR REPLACE FUNCTION f_prep_query (_tbl regclass, _prefix text)
  RETURNS void AS 
$func$
BEGIN
   IF EXISTS (SELECT 1 FROM pg_prepared_statements WHERE name = 'stmt_dyn') THEN
      DEALLOCATE stmt_dyn;
   END IF;                 -- you my or may not need this safety check 

   EXECUTE (
     SELECT 'PREPARE stmt_dyn AS SELECT '
         || string_agg(quote_ident(attname), ',' ORDER BY attname)
         || ' FROM ' || _tbl
      FROM   pg_catalog.pg_attribute
      WHERE  attrelid = _tbl
      AND    attname LIKE _prefix || '%'
      AND    attnum > 0
      AND    NOT attisdropped
     );
END
$func$  LANGUAGE plpgsql;

Ik gebruik regclass voor de tabelnaamparameter _tbl om het ondubbelzinnig en veilig te maken tegen SQLi. Details:

  • Tabelnaam als een PostgreSQL-functieparameter

Het informatieschema bevat niet de oid-kolom van systeemcatalogi, dus schakelde ik over naar pg_catalog.pg_attribute in plaats van information_schema.columns . Dat is ook sneller. Hier zijn voor- en nadelen aan:

  • Hoe te controleren of een tabel in een bepaald schema bestaat

Als een voorbereide verklaring met de naam stmt_dyn bestond al, PREPARE een uitzondering zou maken. Als dat acceptabel is, verwijder dan het vinkje in de systeemweergave pg_prepared_statements en de volgende DEALLOCATE .
Er zijn meer geavanceerde algoritmen mogelijk om meerdere voorbereide instructies per sessie te beheren, of de naam van de voorbereide instructie als extra parameter te nemen, of zelfs een MD5-hash van de queryreeks als naam te gebruiken, maar dat gaat verder dan de reikwijdte van deze vraag.

Houd er rekening mee dat PREPARE opereert buiten het bereik van transacties , eenmaal PREPARE slaagt, bestaat de voorbereide verklaring voor de levensduur van de sessie. Als de inpaktransactie wordt afgebroken, PREPARE is onaangetast. ROLLBACK kan niet verwijder voorbereide verklaringen.

Dynamische query-uitvoering

Twee zoekopdrachten, maar slechts één naar de server bellen. En ook nog eens erg efficiënt.

SELECT f_prep_query('tbl'::regclass, 'pre'::text);
EXECUTE stmt_dyn;

Eenvoudiger en veel efficiënter voor de meeste eenvoudige gebruiksgevallen dan het maken van een tijdelijke tabel of een cursor en daaruit selecteren / ophalen (wat andere opties zouden zijn).

SQL Fiddle.



  1. Uw Time-Series-database schalen - Hoe TimescaleDB eenvoudig te schalen?

  2. PowerManager.PARTIAL_WAKE_LOCK Android

  3. Neem een ​​menselijke benadering van gegevensbeheer

  4. Splits string op spatie en karakter als scheidingsteken in Oracle met regexp_substr