PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Връщане на динамична таблица с неизвестни колони от функцията PL/pgSQL

Това е трудно за решаване, тъй като SQL изисква да знае типа на връщане по време на повикване .
Освен това, функцията plpgsql трябва да има добре дефиниран тип връщане .

Ако изберете да върнете анонимни записи , получавате това, което сте дефинирали:анонимни записи. Postgres не знае какво има вътре. Следователно списък с дефиниции на колони е задължителен за разлагане на типа.

Има различни решения, в зависимост от точните изисквания. Ако имате някакъв начин да разберете типа връщане по време на разговор , предлагамполиморфни типове както е посочено в последната глава на този отговор („Различни типове пълни таблици“):
Рефакторирайте функция PL/pgSQL, за да върнете резултатите от различни SELECT заявки

Но това не обхваща добавянето на друга колона към типа на връщане по време на изпълнение във функцията . Това просто не е възможно. Бихи преразгледал целия подход .

Що се отнася до настоящия ви подход, най-близкото нещо, за което се сещам, е временна таблица (или курсор), която заявявате при второ обаждане в рамките на единична транзакция .

Имате няколко други проблема в кода си . Вижте бележките по-долу.

Доказателство за концепция

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;

Обаждането трябва да бъде в една транзакция. Може да се наложи да започнете изрична транзакция, в зависимост от вашия клиент.

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.

Като алтернатива можете да оставите временната маса да работи за времето на сесията. Все пак внимавайте при сблъсъци с имена с повтарящи се повиквания.

Бележки

  • Използвайте имена на параметри вместо остарелия ALIAS команда.

  • За действително "по подразбиране" за текущата схема, използвайте по-простата заявка, която показвам. Използване на regclass прави трика автоматично. Подробности:

    • Име на таблица като параметър на функцията на PostgreSQL

    В допълнение, това също така избягва синтактични грешки и възможно SQL инжектиране от нестандартни (или злонамерено деформирани) имена на таблици в оригиналния ви код.

  • Кодът във вашия ELSE клаузата изобщо няма да работи.

  • TABLE tbl; е основно съкращение от SELECT * FROM tbl; .

  • Подробности за format() в ръководството.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Мога ли автоматично да създам таблица в PostgreSQL от csv файл със заглавки?

  2. Извадете седмици от дата в PostgreSQL

  3. Как да управлявате вашите PostgreSQL бази данни от ClusterControl CLI

  4. PostgreSQL създаде таблица, ако не съществува

  5. PostgreSQL - заявка от bash скрипт като потребител на база данни 'postgres'