Travailler avec cette table factice
CREATE TEMP TABLE foo (id int, my_num numeric);
INSERT INTO foo VALUES (1, 12.34)
Tout d'abord, j'ai simplifié et aseptisé votre exemple :
-
Suppression de certains bruits sans rapport avec la question.
-
RETURNS SETOF void
n'a guère de sens. J'utiliseRETURNS void
à la place. -
J'utilise
text
au lieu decharacter varying
, juste par souci de simplicité. -
Lorsque vous utilisez SQL dynamique, vous avez pour me prémunir contre l'injection SQL, j'utilise
format()
avec%I
dans ce cas. Il existe d'autres moyens.
Le problème de base est que SQL est très rigide avec les types et les identifiants. Vous travaillez avec tableau dynamique nom ainsi qu'avec nom de champ dynamique d'un enregistrement - un anonyme enregistrer dans votre exemple original. Pl/pgSQL n'est pas bien équipé pour gérer cela. Postgres ne sait pas ce qu'il y a à l'intérieur un enregistrement anonyme. Seulement après avoir attribué l'enregistrement à un type bien connu pouvez-vous référencer des champs individuels.
Voici une question étroitement liée, essayant de définir un champ d'un enregistrement avec un nom dynamique :
Comment définir la valeur d'un champ de variable composite à l'aide de SQL dynamique
Fonction de 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;
Appel :
SELECT * from getrowdata1('foo', 1);
La partie commentée lèverait une exception :
impossible d'identifier la colonne "my_num" dans le type de données d'enregistrement :SELECT * fromgetrowdata(1,'foo')
hstore
Vous devez installer le module supplémentaire hstore pour ça. Une fois par base de données avec :
CREATE EXTENSION hstore;
Alors tout pourrait fonctionner comme ceci :
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;
Appel :
SELECT * from getrowdata2('foo', 1);
Type polymorphe
Alternative sans installer de modules supplémentaires.
Puisque vous sélectionnez une ligne entière dans votre variable d'enregistrement, il existe un type bien défini pour cela par définition. Utilise le. Le mot clé est types polymorphes .
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;
Appel :
SELECT * from getrowdata3(NULL::foo, 1);
-> SQLfiddle
-
J'utilise (ab-)le paramètre d'entrée
_tbl
pour trois fins ici :- Fournit le type bien défini du dossier
- Fournit le nom de la table, automatiquement qualifié par le schéma
- Sert de variable.
-
Plus d'explications dans cette réponse connexe (dernier chapitre) :
Refactoriser une fonction PL/pgSQL pour renvoyer la sortie de diverses requêtes SELECT