sql >> Databasteknik >  >> RDS >> PostgreSQL

Använd textutdata från en funktion som ny fråga

Tricket med PREPARE fungerar inte, eftersom det inte tar en * textsträng* (ett värde) som CREATE FUNCTION gör det, men ett giltigt påstående (kod).

För att konvertera data till körbar kod du måste använda dynamisk SQL, dvs EXECUTE i en plpgsql-funktion eller DO påstående. Detta fungerar utan problem så länge returtypen inte beror på resultatet av den första funktionen myresult() . Annars är du tillbaka för att fånga 22 som beskrivs i mitt tidigare svar:

  • Hur man kör ett strängresultat av en lagrad procedur i postgres

Den avgörande delen är att deklarera returtypen (radtyp i det här fallet) på något sätt. Du kan skapa en TABLE , TEMP TABLE eller TYPE för syftet. Eller så kan du använda ett förberett uttalande eller en refkursor.

Lösning med förberett uttalande

Du har varit väldigt nära. Den saknade pusselbiten är att förbereda den genererade frågan med dynamisk SQL .

Funktion för att förbereda uttalande dynamiskt

Skapa den här funktionen en gång . Det är en optimerad och säker version av 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;

Jag använder regclass för tabellnamnsparametern _tbl för att göra det entydigt och säkert mot SQLi. Detaljer:

  • Tabellnamn som en PostgreSQL-funktionsparameter

Informationsschemat inkluderar inte oid-kolumnen i systemkataloger, så jag bytte till pg_catalog.pg_attribute istället för information_schema.columns . Det är snabbare också. Det finns för- och nackdelar med detta:

  • Hur man kontrollerar om en tabell finns i ett givet schema

Om en förberedd sats med namnet stmt_dyn existerade redan, PREPARE skulle ta upp ett undantag. Om det är acceptabelt, ta bort bocken i systemvyn pg_prepared_statements och följande DEALLOCATE .
Mer sofistikerade algoritmer är möjliga för att hantera flera förberedda satser per session, eller ta namnet på den beredda satsen som ytterligare parameter, eller till och med använda en MD5-hash av frågesträngen som namn, men det är bortom omfattningen av denna fråga.

Tänk på att PREPARE fungerar utanför ramen för transaktioner , en gång PREPARE lyckas, existerar det förberedda uttalandet under sessionens livstid. Om paketeringstransaktionen avbryts, PREPARE är opåverkad. ROLLBACK kan inte ta bort förberedda uttalanden.

Körning av dynamisk fråga

Två frågor, men bara en ringa till servern. Och mycket effektiv också.

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

Enklare och mycket effektivare för de flesta enkla användningsfallen än att skapa en tillfällig tabell eller en markör och välja/hämta från det (vilket skulle vara andra alternativ).

SQL-fiol.



  1. SQL Server 2016 – Introduktion till Stretch Database

  2. Infoga i MySQL från PHP (jQuery/AJAX)

  3. Hur man bäst får någons 'rank' från en poängtabell med php och mysql utan looping

  4. Hur man konverterar UTC till lokal tid i MySQL