Arbejder med dette dummybord
CREATE TEMP TABLE foo (id int, my_num numeric);
INSERT INTO foo VALUES (1, 12.34)
Først forenklede og rensede jeg dit eksempel:
-
Fjernet noget støj, der er irrelevant for spørgsmålet.
-
RETURNS SETOF void
giver næppe mening. Jeg brugerRETURNS void
i stedet. -
Jeg bruger
text
i stedet forcharacter varying
, bare for nemhedens skyld. -
Når du bruger dynamisk SQL, har du for at sikre mig mod SQL-injektion, bruger jeg
format()
med%I
I dette tilfælde. Der er andre måder.
Det grundlæggende problem er, at SQL er meget rigid med typer og identifikatorer. Du arbejder med dynamisk tabel navn samt med dynamisk feltnavn på en post - en anonym optage i dit originale eksempel. Pl/pgSQL er ikke godt rustet til at håndtere dette. Postgres ved ikke, hvad der er inde i en anonym registrering. Først efter at du har tildelt posten til en velkendt type kan du henvise til individuelle felter.
Her er et nært beslægtet spørgsmål, der prøver at indstille et felt i en post med dynamisk navn:
Sådan indstilles værdien af det sammensatte variabelfelt ved hjælp af dynamisk SQL
Grundlæggende 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 til:
SELECT * from getrowdata1('foo', 1);
Den kommenterede del ville rejse en undtagelse:
kunne ikke identificere kolonne "my_num" i postdatatypen:SELECT * fromgetrowdata(1,'foo')
hstore
Du skal installere det ekstra modul hstore for det. Én gang pr. database med:
CREATE EXTENSION hstore;
Så kunne alt fungere sådan her:
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 til:
SELECT * from getrowdata2('foo', 1);
Polymorf type
Alternativ uden at installere yderligere moduler.
Da du vælger en hel række i din registreringsvariabel, er der en veldefineret type for det per definition. Brug det. Nøgleordet er polymorfe 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 til:
SELECT * from getrowdata3(NULL::foo, 1);
-> SQLfiddle
-
Jeg (ab-)bruger inputparameteren
_tbl
for tre formål her:- Giver den veldefinerede type rekorden
- Giver navnet i tabellen, automatisk skemakvalificeret
- Virker som variabel.
-
Mere forklaring i dette relaterede svar (sidste kapitel):
Refaktorer en PL/pgSQL-funktion for at returnere output fra forskellige SELECT-forespørgsler