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

Динамично предаване на имена на колони за променлива на запис в PostgreSQL

Работа с тази фиктивна таблица

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

Първо, опростих и почистих вашия пример:

  • Премахнат някои шумове, които не са от значение за въпроса.

  • RETURNS SETOF void едва ли има смисъл. Използвам RETURNS void вместо това.

  • Използвам text вместо character varying , само за простота.

  • Когато използвате динамичен SQL, имате за да се предпазя от SQL инжектиране, използвам format() с %I в такъв случай. Има и други начини.

Основният проблем е, че SQL е много твърд с типове и идентификатори. Вие работите с динамична таблица име, както и с име на динамично поле на запис - нанонимен записа в оригиналния си пример. Pl/pgSQL не е добре оборудван да се справи с това. Postgres не знае какво е вътре анонимен запис. Само след като присвоите записа към добре познат тип можете ли да препратите отделни полета.
Ето един тясно свързан въпрос, опитвайки се да зададете поле на запис с динамично име:
Как да зададете стойност на полето за съставна променлива с помощта на динамичен SQL

Основна функция

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;

Обадете се:

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

Коментираната част би предизвикала изключение:

не можа да идентифицира колона "my_num" в тип данни на записа:SELECT * fromgetrowdata(1,'foo')

hstore

Трябва да инсталирате допълнителния модул hstore за това. Веднъж на база данни с:

CREATE EXTENSION hstore;

Тогава всичко може да работи така:

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;

Обадете се:

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

Полиморфен тип

Алтернатива без инсталиране на допълнителни модули.

Тъй като избирате цял ред във вашата променлива за запис, има добре дефиниран тип за него по дефиниция. Използваи го. Ключовата дума е полиморфни типове .

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;

Обадете се:

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

-> SQLfiddle

  • (ab-) използвам входния параметър _tbl за три цели тук:

    • Осигурява добре дефинирания тип на записа
    • Предоставя име на таблицата, автоматично квалифицирани по схема
    • Служи като променлива.
  • Повече обяснение в този свързан отговор (последна глава):
    Рефакторирайте функция PL/pgSQL, за да върнете изхода от различни 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. Изберете тип данни на полето в postgres

  2. Динамичен SQL (EXECUTE) като условие за оператор IF

  3. Разплитане на надстройката на PostgreSQL

  4. Конвертирайте PostgreSQL масив в PHP масив

  5. Postgres е най-готината база данни – Причина №1:Разработчиците я обичат!