sql >> Databáze >  >> RDS >> PostgreSQL

Vrátí dynamickou tabulku s neznámými sloupci z funkce PL/pgSQL

To je těžké vyřešit, protože SQL vyžaduje znát návratový typ v době volání .
Funkce plpgsql také musí mít dobře definovaný návratový typ .

Pokud se rozhodnete vrátit anonymní záznamy , získáte to, co jste definovali:anonymní záznamy. Postgres neví, co je uvnitř. Proto je vyžadován seznam definic sloupců rozložit typ.

V závislosti na přesných požadavcích existují různá řešení. Pokud máte nějaký způsob, jak zjistit typ návratu v době hovoru , navrhuji polymorfní typy jak je uvedeno v poslední kapitole této odpovědi („Různé kompletní typy tabulek“):
Refaktorujte funkci PL/pgSQL, aby vrátila výstup různých SELECT dotazů

To však nezahrnuje přidání dalšího sloupce do návratového typu za běhu uvnitř funkce . To prostě není možné. Přehodnotil bych celý přístup .

Pokud jde o váš současný přístup, nejbližší věc, kterou mě napadá, by byla dočasná tabulka (nebo kurzor), kterou zadáte při druhém volání v rámci jediné transakce .

V kódu máte několik dalších problémů . Viz poznámky níže.

Důkaz konceptu

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;

Hovor musí být v jedné transakci. V závislosti na vašem klientovi možná budete muset zahájit explicitní transakci.

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.

Alternativně můžete nechat dočasnou tabulku v provozu po dobu trvání relace. Dejte si však pozor na kolize pojmenování při opakovaných hovorech.

Poznámky

  • Používejte názvy parametrů namísto zastaralých ALIAS příkaz.

  • Chcete-li skutečně "výchozí" aktuální schéma, použijte jednodušší dotaz, který zobrazím. Pomocí regclass dělá trik automaticky. Podrobnosti:

    • Název tabulky jako parametr funkce PostgreSQL

    Kromě toho se tím také zabrání syntaktickým chybám a možnému vložení SQL z nestandardních (nebo škodlivě zdeformovaných) názvů tabulek ve vašem původním kódu.

  • Kód ve vašem ELSE klauzule by vůbec nefungovala.

  • TABLE tbl; je v podstatě zkratka pro SELECT * FROM tbl; .

  • Podrobnosti o format() v návodu.




  1. Jak mám uložit GUID v tabulkách MySQL?

  2. Oracle Security Alert pro CVE-2021-44228

  3. Jak klastrovat vaše nástroje pro vyrovnávání zatížení ProxySQL

  4. SQL DELETE s JOIN další tabulkou pro podmínku WHERE