sql >> Database >  >> RDS >> PostgreSQL

Dynamische tabel retourneren met onbekende kolommen van de PL/pgSQL-functie

Dit is moeilijk op te lossen, omdat SQL vereist om het retourtype te weten tijdens de oproep .
Ook moet een plpgsql-functie een goed gedefinieerd retourtype hebben .

Als u ervoor kiest om anonieme records te retourneren , krijg je wat je hebt gedefinieerd:anonieme records. Postgres weet niet wat erin zit. Daarom is een kolomdefinitielijst vereist om het type te ontleden.

Er zijn verschillende oplossingen, afhankelijk van de exacte vereisten. Als je een manier hebt om het retourtype te weten tijdens het bellen , stel ik polymorfe typen voor zoals uiteengezet in het laatste hoofdstuk van dit antwoord ("Verschillende complete tabeltypen"):
Refactor een PL/pgSQL-functie om de uitvoer van verschillende SELECT-query's te retourneren

Maar dat dekt niet het toevoegen van een andere kolom aan het retourtype at runtime binnen de functie . Dat kan gewoon niet. Ik zou de hele aanpak heroverwegen .

Wat betreft uw huidige aanpak, het dichtst in de buurt dat ik kan bedenken, is een tijdelijke tabel (of een cursor), die u in een tweede oproep opvraagt binnen een enkele transactie .

Je hebt een aantal andere problemen met je code . Zie onderstaande opmerkingen.

Bewijs van concept

CREATE OR REPLACE FUNCTION f_tbl_plus_infowindow (_tbl regclass) -- regclass!
  RETURNS void AS  -- no direct return type
$func$
DECLARE
   -- appending _tmp for temp table
   _tmp text := quote_ident(_tbl::text || '_tmp');
BEGIN

-- Create temp table only for duration of transaction
EXECUTE format(
   'CREATE TEMP TABLE %s ON COMMIT DROP AS TABLE %s LIMIT 0', _tmp, _tbl);

IF EXISTS (
   SELECT 1
   FROM   pg_attribute a
   WHERE  a.attrelid = _tbl
   AND    a.attname  = 'infowindow'
   AND    a.attisdropped = FALSE)
THEN
   EXECUTE format('INSERT INTO %s SELECT * FROM %s', _tmp, _tbl);
ELSE
  -- This is assuming a NOT NULL column named "id"!
   EXECUTE format($x$
      ALTER  TABLE %1$s ADD COLUMN infowindow text;
      INSERT INTO %1$s
      SELECT *, 'ID: ' || id::text
      FROM   %2$s $x$
     ,_tmp, _tbl);
END IF;

END
$func$ LANGUAGE plpgsql;

De oproep moet in een enkele transactie zijn. Het kan zijn dat u een expliciete transactie moet starten, afhankelijk van uw klant.

BEGIN;
SELECT f_tbl_plus_infowindow ('tbl');
SELECT * FROM tbl_tmp;  -- do something with the returned rows
ROLLBACK;               -- or COMMIT, does not matter here

SQL Fiddle.

Als alternatief kunt u de tijdelijke tafel laten leven voor de duur van de sessie. Wees echter op uw hoede voor het benoemen van botsingen met herhaalde oproepen.

Opmerkingen

  • Gebruik parameternamen in plaats van de verouderde ALIAS commando.

  • Gebruik de eenvoudigere query die ik weergeef om daadwerkelijk "standaard" te zijn in het huidige schema. regclass gebruiken doet de truc automatisch. Details:

    • Tabelnaam als een PostgreSQL-functieparameter

    Bovendien vermijdt dit ook syntaxisfouten en mogelijke SQL-injectie van niet-standaard (of kwaadwillig misvormde) tabelnamen in je originele code.

  • De code in uw ELSE clausule zou helemaal niet werken.

  • TABLE tbl; is in feite een afkorting voor SELECT * FROM tbl; .

  • Details over format() in de handleiding.




  1. Gegevens ophalen uit sql-dabse in flutter datewise?

  2. Hoe de MID()-functie werkt in MySQL

  3. Oracle:Null- en niet-null-rijen tellen

  4. Toepassingen die op Linux draaien verbinden met Amazon Relational Database Services (RDS) voor SQL Server