Je développe une fonction définie par l'utilisateur qui prend deux arguments:
create or replace function gesio(
events_table_in regclass,
events_table_out regclass)
returns void as $$ ... $$
events_table_in
et events_table_out
avoir exactement le même schéma.
Il suffit d'expliquer, je boucle à travers les archives de events_table_in
, manipuler les enregistrements et souhaiter appendez (insérer) les enregistrements manipulés dans events_table_out
à la mode suivante:
OPEN recCurs FOR execute
format('SELECT * FROM %s order by session_id, event_time', event_table_in);
LOOP
FETCH recCurs into rec;
if not found then
exit;
end if;
-- 1. do something with rec
-- 2. insert the rec into events_table_out
end loop;
Comment puis-je enregistrer le rec
dans events_table_out
?
Là est une solution avec juste pl/pgsql. Simple et élégant aussi. Des choses assez avancées, cependant.
[.____] nécessite des postgres 9. ou ultérieure (solution de contournement pour les versions plus anciennes possibles).
CREATE OR REPLACE FUNCTION gesio(_tbl_in anyelement, _tbl_out regclass)
RETURNS void AS
$func$
BEGIN
FOR _tbl_in IN EXECUTE
format('SELECT * FROM %s', pg_typeof(_tbl_in))
LOOP
-- do something with record
EXECUTE format('INSERT INTO %s SELECT $1.*', _tbl_out)
USING _tbl_in;
END LOOP;
END
$func$ LANGUAGE plpgsql;
Appel (important!):
SELECT gesio(NULL::t, 't1');
t
et t1
étant les tables avec un schéma identique.
Note que le paramètre polymorphe (anyelement
) n'est nécessaire que si vous avez besoin de la valeur ou du type de données pour le calcul dans l'organisme de fonction. Sinon, vous pouvez simplifier comme démontré dans cette réponse ultérieure:
curseur implicite d'une boucle FOR
au lieu d'un curseur explicite. C'est généralement préférable.
Un obstacle à surmonter est que les variables à l'intérieur de la fonction ne peuvent pas être définies comme de type polymorphique anyelement
(pourtant). Cette réponse connexe sur SO explique la solution. Fournit un Solution de contournement pour les anciennes versions aussi.
Je traitne dans une valeur NULL
valeur de type t
, qui dessert trois objectifs:
Le valeur du premier paramètre est Jeté. Utilisez NULL
.
Envisager cette réponse connexe sur SO avec plus de détails . La partie la plus intéressante étant le dernier chapitre Divers types de table complets.
Si vos calculs ne sont pas trop sophistiqués, vous pourrez peut-être remplacer la boucle avec une seule instruction SQL dynamique, qui est généralement plus rapide.
Malheureusement, il n'est pas facile d'analyser le type RECORD
utilisant pl/pgsql. Si la structure des tables passées dans des arguments est toujours la même que celle d'une autre table ou de type, vous pouvez utiliser ce type directement au lieu de RECORD
, puis utilisez ce qui suit:
DECLARE
recCurs table_or_type;
...
BEGIN
...
OPEN recCurs FOR EXECUTE ...
...
EXECUTE 'INSERT INTO ' || events_table_out || ' VALUES(($1).*)'
USING recCurs;
...
Mais cela ne fonctionnera pas avec RECORD
type. La seule solution à laquelle je puisse penser, c'est la création de la requête à la main. Mais PL/PGSQL ne donne aucun moyen d'obtenir de manière dynamique les clés d'un type RECORD
. Vous devez donc utiliser certains outils externes. Le meilleur (à mon avis) pour ce type de travail est le hstore
extension . Une fois installé, vous pouvez le créer sur votre base de données (les travaux suivants que sur 9.1+, pour plus tôt, vous devez le faire à la main):
CREATE EXTENSION hstore;
À présent. Vous êtes capable de convertir un type RECORD
Tapez sur un type hstore
Type, à l'aide de hstore(recCurs)
, vous pouvez ainsi iTER de manière dynamique sur ses clés et valeurs avec le each
une fonction:
DECLARE
recCurs record;
kv record;
v_cols text;
v_vals text;
BEGIN
...
OPEN recCurs FOR EXECUTE ...
...
-- 1. do something with rec
-- 2. insert the rec into events_table_out:
v_cols := '';
v_vals := '';
FOR kv IN SELECT * FROM each(hstore(recCurs)) LOOP
v_cols := v_cols || kv.key || ',';
v_vals := v_vals || quote_nullable(kv.value) || ',';
END LOOP;
v_cols := substr(v_cols, 1, length(v_cols)-1);
v_vals := substr(v_vals, 1, length(v_vals)-1);
EXECUTE 'INSERT INTO ' || events_table_out
|| '(' || v_cols || ') '
|| 'VALUES (' || v_vals || ')';
...
Bien sûr, cela ne fonctionnera que si la table "pointée" par events_table_out
A toutes les colonnes que events_table_in
A (la première peut avoir plus de colonnes).
Reprise: Vous voulez toujours un type de données de clé/valeur dynamique sur PL/PGSQL, et RECORD
ne suffit pas, le hstore
peut être utilisé.