sql >> Database >  >> RDS >> PostgreSQL

Dynamisch doorgeven van kolomnamen voor een recordvariabele in PostgreSQL

Werken met deze dummy-tabel

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

Eerst heb ik je voorbeeld vereenvoudigd en opgeschoond:

  • Wat ruis verwijderd die niet relevant is voor de vraag.

  • RETURNS SETOF void nauwelijks zin. Ik gebruik RETURNS void in plaats daarvan.

  • Ik gebruik text in plaats van character varying , gewoon voor de eenvoud.

  • Wanneer u dynamische SQL gebruikt, heeft u om te beschermen tegen SQL-injectie, gebruik ik format() met %I in dit geval. Er zijn andere manieren.

Het basisprobleem is dat SQL erg rigide is met typen en identifiers. U werkt met dynamische tabel naam en met dynamische veldnaam van een record - een anoniem opnemen in je originele voorbeeld. Pl/pgSQL is niet goed uitgerust om hiermee om te gaan. Postgres weet niet wat er in zit een anonieme opname. Pas nadat u het record heeft toegewezen aan een bekend type kun je naar individuele velden verwijzen.
Hier is een nauw verwante vraag, die probeert te in te stellen een veld van een record met dynamische naam:
Hoe de waarde van het samengestelde variabele veld in te stellen met behulp van dynamische SQL

Basisfunctie

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;

Bel:

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

Het becommentarieerde gedeelte zou een uitzondering opleveren:

kon kolom "my_num" niet identificeren in recordgegevenstype:SELECT * fromgetrowdata(1,'foo')

hstore

U moet de extra module hstore . installeren voor deze. Eenmaal per database met:

CREATE EXTENSION hstore;

Dan zou alles zo kunnen werken:

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;

Bel:

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

Polymorf type

Alternatief zonder extra modules te installeren.

Aangezien u een hele rij in uw recordvariabele selecteert, is er een goed gedefinieerd type daarvoor per definitie. Gebruik het. Het sleutelwoord is polymorfe typen .

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;

Bel:

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

-> SQLfiddle

  • Ik (ab-)gebruik de invoerparameter _tbl voor drie doeleinden hier:

    • Biedt het goed gedefinieerde type van het record
    • Geeft de naam van de tabel, automatisch schema-gekwalificeerd
    • Werkt als variabele.
  • Meer uitleg in dit gerelateerde antwoord (laatste hoofdstuk):
    Refactor een PL/pgSQL-functie om de uitvoer van verschillende SELECT-query's te retourneren




  1. Oracle-triggerfout ORA-04091

  2. Beveiligingsoverwegingen voor MariaDB-implementaties in een hybride cloudomgeving

  3. Reverse Engineering van een MySQL-database met MySQL Workbench

  4. Een tijdelijke tabel maken in SQL Server