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

Dynamiczne przekazywanie nazw kolumn dla zmiennej rekordu w PostgreSQL

Praca z tym atrapą stołu

CREATE TEMP TABLE foo (id int, my_num numeric);
INSERT INTO foo VALUES (1, 12.34)

Najpierw uprościłem i oczyściłem twój przykład:

  • Usunięto szum, który nie ma związku z pytaniem.

  • RETURNS SETOF void prawie nie ma sensu. Używam RETURNS void zamiast tego.

  • Używam text zamiast character varying , tylko ze względu na prostotę.

  • Używając dynamicznego SQL, masz aby zabezpieczyć się przed wstrzyknięciem SQL, używam format() z %I w tym przypadku. Są inne sposoby.

Podstawowym problemem jest to, że SQL jest bardzo sztywny pod względem typów i identyfikatorów. Korzystasz z tabeli dynamicznej nazwę oraz nazwę pola dynamicznego rekordu - anonimowy nagraj w twoim oryginalnym przykładzie. Pl/pgSQL nie jest dobrze przygotowany do radzenia sobie z tym problemem. Postgres nie wie, co jest w środku anonimowy rekord. Dopiero po przypisaniu rekordu do dobrze znanego typu czy możesz odwołać się do poszczególnych pól.
Oto ściśle powiązane pytanie, próbując ustawić pole rekordu z dynamiczną nazwą:
Jak ustawić wartość pola zmiennej złożonej za pomocą dynamicznego SQL

Funkcja podstawowa

CREATE OR REPLACE FUNCTION getrowdata1(table_name text, id int)
  RETURNS void AS
$func$ 
DECLARE
   srowdata record;
   reqfield text := 'my_num';   -- assigning at declaration time for convenience
   value    numeric;
BEGIN

RAISE NOTICE 'id: %', id; 

EXECUTE format('SELECT * FROM %I WHERE id = $1', table_name)
USING  id
INTO   srowdata;

RAISE NOTICE 'srowdata: %', srowdata;

RAISE NOTICE 'srowdatadata.my_num: %', srowdata.my_num;

/* This does not work, even with dynamic SQL
EXECUTE format('SELECT ($1).%I', reqfield)
USING srowdata
INTO value;

RAISE NOTICE 'value: %', value;
*/

END
$func$ LANGUAGE plpgsql;

Zadzwoń:

SELECT * from getrowdata1('foo', 1);

Komentowana część wywołałaby wyjątek:

nie można zidentyfikować kolumny "my_num" w typie danych rekordu:SELECT * fromgetrowdata(1,'foo')

hstore

Musisz zainstalować dodatkowy moduł hstore dla tego. Raz na bazę danych z:

CREATE EXTENSION hstore;

Wtedy wszystko mogłoby działać tak:

CREATE OR REPLACE FUNCTION getrowdata2(table_name text, id int)
  RETURNS void AS
$func$ 
DECLARE
   hstoredata hstore;
   reqfield   text := 'my_num';
   value      numeric;
BEGIN

RAISE NOTICE 'id: %', id; 

EXECUTE format('SELECT hstore(t) FROM %I t WHERE id = $1', table_name)
USING  id
INTO   hstoredata;

RAISE NOTICE 'hstoredata: %', hstoredata;

RAISE NOTICE 'hstoredata.my_num: %', hstoredata -> 'my_num';

value := hstoredata -> reqfield;

RAISE NOTICE 'value: %', value;

END
$func$ LANGUAGE plpgsql;

Zadzwoń:

SELECT * from getrowdata2('foo', 1);

Typ polimorficzny

Alternatywa bez instalowania dodatkowych modułów.

Ponieważ wybierasz cały wiersz do zmiennej rekordu, istnieje dobrze zdefiniowany typ za to z definicji. Użyj tego. Słowo kluczowe to typy polimorficzne .

CREATE OR REPLACE FUNCTION getrowdata3(_tbl anyelement, id int)
  RETURNS void AS
$func$ 
DECLARE
   reqfield text := 'my_num';
   value    numeric;
BEGIN

RAISE NOTICE 'id: %', id; 

EXECUTE format('SELECT * FROM %s WHERE id = $1', pg_typeof(_tbl))
USING  id
INTO   _tbl;

RAISE NOTICE '_tbl: %', _tbl;

RAISE NOTICE '_tbl.my_num: %', _tbl.my_num;

EXECUTE 'SELECT ($1).' || reqfield   -- requfield must be SQLi-safe or escape
USING _tbl
INTO  value;

RAISE NOTICE 'value: %', value;

END
$func$ LANGUAGE plpgsql;

Zadzwoń:

SELECT * from getrowdata3(NULL::foo, 1);

-> SQLfiddle

  • Używam (ab-) parametru wejściowego _tbl dla trzech cele tutaj:

    • Zapewnia dobrze zdefiniowany typ rekordu
    • Zapewnia nazwę tabeli, automatycznie kwalifikowana według schematu
    • Służy jako zmienna.
  • Więcej wyjaśnień w tej powiązanej odpowiedzi (ostatni rozdział):
    Refaktoryzuj funkcję PL/pgSQL, aby zwrócić dane wyjściowe różnych zapytań SELECT




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Dodaj dni do daty w PostgreSQL

  2. Wymień wszystkie nazwy indeksów, nazwy kolumn i ich nazwę tabeli bazy danych PostgreSQL

  3. Jak wstawić dane do tabeli za pomocą procedur składowanych w postgresql

  4. Jak uzyskać klucze podstawowe tabeli z Postgres za pośrednictwem plpgsql?

  5. UUID czy SEQUENCE dla klucza podstawowego?