Lavorare con questa tabella fittizia
CREATE TEMP TABLE foo (id int, my_num numeric);
INSERT INTO foo VALUES (1, 12.34)
Per prima cosa, ho semplificato e sanificato il tuo esempio:
-
Rimosso un po' di rumore irrilevante per la domanda.
-
RETURNS SETOF void
ha poco senso. UsoRETURNS void
invece. -
Uso
text
invece dicharacter varying
, solo per semplicità. -
Quando usi l'SQL dinamico, hai per proteggermi dall'iniezione SQL, utilizzo
format()
con%I
in questo caso. Ci sono altri modi.
Il problema di base è che SQL è molto rigido con tipi e identificatori. Stai operando con tabella dinamica nome e con nome campo dinamico di un record - un anonimo registrare nel tuo esempio originale. Pl/pgSQL non è ben attrezzato per affrontare questo. Postgres non sa cosa c'è dentro un record anonimo. Solo dopo aver assegnato il record a un tipo noto puoi fare riferimento a singoli campi.
Ecco una domanda strettamente correlata, cercando di impostare un campo di un record con nome dinamico:
Come impostare il valore di un campo variabile composto utilizzando SQL dinamico
Funzione di base
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;
Chiama:
SELECT * from getrowdata1('foo', 1);
La parte commentata solleverebbe un'eccezione:
impossibile identificare la colonna "my_num" nel tipo di dati del record:SELECT * fromgetrowdata(1,'foo')
hstore
Devi installare il modulo aggiuntivo hstore per questo. Una volta per database con:
CREATE EXTENSION hstore;
Allora tutto potrebbe funzionare così:
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;
Chiama:
SELECT * from getrowdata2('foo', 1);
Tipo polimorfico
Alternativa senza installare moduli aggiuntivi.
Poiché selezioni un'intera riga nella variabile del record, esiste un tipo ben definito per esso per definizione. Usalo. La parola chiave è tipi polimorfici .
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;
Chiama:
SELECT * from getrowdata3(NULL::foo, 1);
-> SQLfiddle
-
Io (ab-) utilizzo il parametro di input
_tbl
per tre scopi qui:- Fornisce il tipo ben definito del record
- Fornisce il nome della tabella, qualificato automaticamente per schema
- Serve come variabile.
-
Maggiori spiegazioni in questa risposta correlata (ultimo capitolo):
Refactoring di una funzione PL/pgSQL per restituire l'output di varie query SELECT