sql >> Database teknologi >  >> RDS >> PostgreSQL

Brug tekstoutput fra en funktion som ny forespørgsel

Tricket med PREPARE virker ikke, da det ikke tager en * tekststreng* (en værdi) som CREATE FUNCTION gør, men en gyldig erklæring (kode).

At konvertere data i eksekverbar kode du skal bruge dynamisk SQL, dvs. EXECUTE i en plpgsql-funktion eller DO udmelding. Dette fungerer uden problemer, så længe returtypen ikke afhænger af resultatet af den første funktion myresult() . Ellers er du tilbage for at fange 22 som skitseret i mit tidligere svar:

  • Sådan udføres et strengresultat af en lagret procedure i postgres

Den afgørende del er at erklære returtypen (rækketype i dette tilfælde) på en eller anden måde. Du kan oprette en TABLE , TEMP TABLE eller TYPE til formålet. Eller du kan bruge en forberedt erklæring eller en refcursor.

Løsning med forberedt redegørelse

Du har været meget tæt på. Den manglende brik i puslespillet er at forberede den genererede forespørgsel med dynamisk SQL .

Funktion til at forberede sætning dynamisk

Opret denne funktion én gang . Det er en optimeret og sikker version af din funktion 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;

Jeg bruger regclass for tabelnavnsparameteren _tbl at gøre det entydigt og sikkert mod SQLi. Detaljer:

  • Tabelnavn som en PostgreSQL-funktionsparameter

Informationsskemaet inkluderer ikke oid-kolonnen i systemkataloger, så jeg skiftede til pg_catalog.pg_attribute i stedet for information_schema.columns . Det er også hurtigere. Der er fordele og ulemper ved dette:

  • Sådan kontrollerer du, om en tabel findes i et givet skema

Hvis en forberedt sætning med navnet stmt_dyn eksisterede allerede, PREPARE ville rejse en undtagelse. Hvis det er acceptabelt, skal du fjerne markeringen på systemvisningen pg_prepared_statements og følgende DEALLOCATE .
Mere sofistikerede algoritmer er mulige til at administrere flere forberedte sætninger pr. session, eller tage navnet på den forberedte sætning som en ekstra parameter, eller endda bruge en MD5-hash af forespørgselsstrengen som navn, men det er ud over omfanget af dette spørgsmål.

Vær opmærksom på, at PREPARE opererer uden for rammerne af transaktioner , én gang PREPARE lykkes, eksisterer den forberedte erklæring i hele sessionens levetid. Hvis indpakningstransaktionen afbrydes, PREPARE er upåvirket. ROLLBACK kan ikke fjerne udarbejdede erklæringer.

Dynamisk udførelse af forespørgsler

To forespørgsler, men kun én opkald til serveren. Og også meget effektiv.

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

Enklere og meget mere effektivt til de fleste simple brugstilfælde end at oprette en midlertidig tabel eller en markør og vælge/hente fra den (hvilket ville være andre muligheder).

SQL Fiddle.



  1. Returner ISO-ugenummeret fra en dato i SQL Server (T-SQL)

  2. Hvad har poker, blackjack, Belot og Préférence med databaser at gøre?

  3. Sådan får du den aktuelle dato i SQLite

  4. I Apache Spark 2.0.0, er det muligt at hente en forespørgsel fra en ekstern database (i stedet for at få fat i hele tabellen)?