web-dev-qa-db-fra.com

Insérer des valeurs d'une variable d'enregistrement dans une table

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?

3
arthur

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:

Ingrédients majeurs

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:

  • Fournir un nom de table.
  • Fournir du type de table.
  • Servir de variable de boucle.

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.

SQL Fiddle Demo.

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.

9

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é.

5
MatheusOl