sql >> Databasteknik >  >> RDS >> PostgreSQL

Skicka kolumnnamn dynamiskt för en postvariabel i PostgreSQL

Arbeta med det här dummybordet

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

Först förenklade och sanerade jag ditt exempel:

  • Tog bort en del brus som är irrelevant för frågan.

  • RETURNS SETOF void är knappast vettigt. Jag använder RETURNS void istället.

  • Jag använder text istället för att character varying , bara för enkelhetens skull.

  • När du använder dynamisk SQL har för att skydda mot SQL-injektion använder jag format() med %I I detta fall. Det finns andra sätt.

Det grundläggande problemet är att SQL är väldigt stel med typer och identifierare. Du arbetar med dynamisk tabell namn samt med dynamiskt fältnamn på en post - en anonym spela in i ditt ursprungliga exempel. Pl/pgSQL är inte väl rustad för att hantera detta. Postgres vet inte vad som finns inuti ett anonymt register. Först efter att du tilldelat posten till en välkänd typ kan du referera till enskilda fält.
Här är en närbesläktad fråga som försöker ställa ett fält i en post med dynamiskt namn:
Hur man ställer in värdet på ett sammansatt variabelfält med hjälp av dynamisk SQL

Grundläggande funktion

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;

Ring:

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

Den kommenterade delen skulle ta upp ett undantag:

kunde inte identifiera kolumnen "my_num" i postens datatyp:SELECT * fromgetrowdata(1,'foo')

hstore

Du måste installera tilläggsmodulen hstore för detta. En gång per databas med:

CREATE EXTENSION hstore;

Då skulle allt kunna fungera så här:

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;

Ring:

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

Polymorf typ

Alternativ utan att installera ytterligare moduler.

Eftersom du väljer en hel rad i din postvariabel finns det en väldefinierad typ för det per definition. Använd den. Nyckelordet är polymorfa typer .

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;

Ring:

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

-> SQLfiddle

  • Jag (ab-)använder indataparametern _tbl för tre syften här:

    • Tillhandahåller den väldefinierade typen protokollet
    • Tillhandahåller namnet i tabellen, automatiskt schemakvalificerad
    • Fungerar som variabel.
  • Mer förklaring i detta relaterade svar (sista kapitlet):
    Refaktorera en PL/pgSQL-funktion för att returnera utdata från olika SELECT-frågor




  1. Skapar en främmande nyckel automatiskt ett index?

  2. MariaDB JSON_COMPACT() Förklarad

  3. beräkna löpande saldo i Oracle-fråga

  4. Hur man får det aktuella datumet i SQL Server