PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Zwróć dynamiczną tabelę z nieznanymi kolumnami z funkcji PL/pgSQL

Jest to trudne do rozwiązania, ponieważ SQL wymaga znajomości typu zwracanego w czasie połączenia .
Ponadto funkcja plpgsql musi mieć dobrze zdefiniowany typ zwracany .

Jeśli zdecydujesz się zwrócić anonimowe rekordy , otrzymujesz to, co zdefiniowałeś:anonimowe rekordy. Postgres nie wie, co jest w środku. Dlatego lista definicji kolumn jest wymagana rozłożyć typ.

Istnieją różne obejścia, w zależności od dokładnych wymagań. Jeśli możesz w jakikolwiek sposób poznać typ zwrotu w czasie połączenia , proponuję typy polimorficzne jak opisano w ostatnim rozdziale tej odpowiedzi ("Różne typy kompletnych tabel"):
Refaktoryzacja funkcji PL/pgSQL w celu zwrócenia wyników różnych zapytań SELECT

Ale to nie obejmuje dodawania kolejnej kolumny do zwracanego typu w czasie wykonywania wewnątrz funkcji . To po prostu niemożliwe. Chciałbym przemyśleć całe podejście .

Jeśli chodzi o twoje obecne podejście, najbliższą rzeczą, o której mogę pomyśleć, byłaby tabela tymczasowa (lub kursor), do której wysyłasz zapytanie w drugim wywołaniu w ramach pojedynczej transakcji .

Masz kilka innych problemów w swoim kodzie . Zobacz uwagi poniżej.

Dowód koncepcji

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;

Połączenie musi być w ramach jednej transakcji. Być może będziesz musiał rozpocząć wyraźną transakcję, w zależności od klienta.

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

Skrzypce SQL.

Alternatywnie możesz pozostawić tymczasową tabelę na żywo na czas trwania sesji. Uważaj jednak na kolizje nazw przy powtarzających się wywołaniach.

Notatki

  • Użyj nazw parametrów zamiast przestarzałego ALIAS polecenie.

  • Aby faktycznie „domyślnie” w bieżącym schemacie, użyj prostszego zapytania, które wyświetlam. Korzystanie z regclass robi sztuczkę automatycznie. Szczegóły:

    • Nazwa tabeli jako parametr funkcji PostgreSQL

    Ponadto pozwala to również uniknąć błędów składniowych i możliwego wstrzyknięcia SQL z niestandardowych (lub złośliwie zniekształconych) nazw tabel w oryginalnym kodzie.

  • Kod w Twoim ELSE klauzula w ogóle nie zadziała.

  • TABLE tbl; jest w zasadzie skrótem od SELECT * FROM tbl; .

  • Szczegóły dotyczące format() w instrukcji.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Wyświetl wszystkie tabele w postgresql information_schema

  2. Oracle do PostgreSQL:ROZPOCZNIJ OD/POŁĄCZ PRZEZ

  3. 5 sposobów na sprawdzenie, czy tabela istnieje w PostgreSQL

  4. Zmodyfikuj wartość początkową AutoField Django

  5. Połącz wiele instrukcji SELECT