2009-08-28 9 views
0

Je souhaite stocker les résultats de test sous la forme d'un fichier .CSV dans une base de données centrale. Le DB a un schéma qui ne correspond pas au fichier CSV. Idéalement, je passerais simplement le contenu du fichier CSV sous forme de chaîne directement au serveur et aurais une fonction qui pourrait transformer le fichier CSV en une table pouvant être jointe et utilisée dans un INSERT.Passer un paramètre CSV dans Postgres

Voici un exemple simplifié:

INSERT INTO MyTable 
SELECT * 
FROM parse_csv(csv_data) t 
INNER JOIN Categories c 
ON t.CatID=c.CatID 

Note: csv_data est un varchar avec le contenu d'un fichier CSV. Notez également l'opération INNER JOIN effectuée directement sur la sortie de parse_csv. Quel est un moyen rapide d'écrire la fonction parse_csv() ci-dessus?

Je pense aussi à utiliser OPENXML et passer une chaîne XML, mais je ne trouve pas cette fonction sur Postgres. Voir this question. Je préfère ne pas analyser le fichier CSV dans le code de l'application et appeler INSERT un millier de fois. Cela pourrait être beaucoup d'allers-retours inutiles. Je connais la fonction COPY, mais j'ai plusieurs fichiers CSV du même format que je ne veux pas entrer en collision.

Je suis ouvert à toutes suggestions ou conseils.

Répondre

1

Il est possible que vos données CSV aient un format connu.

Supposons qu'ils ont 2 colonnes: "CatID" et "WhatEver".

Maintenant, vous pouvez créer une fonction pl/perlu (ou pl/PythonU, ou avec un peu de chance, même pl/pgsql, mais il serait difficile):

create type parse_csv_srf as (catid int4, whatever text); 
create function parse_csv(text) returns setof parse_csv_srf as $$ 
... 
$$ language plperlu; 

corps de la fonction est en Perl (ou Python, ou autre) et fait l'analyse, plus renvoie (dans le cas de Perl) une référence au tableau de hashrefs, où chaque hash devrait avoir les clés "catid" et "whatever" avec quelques valeurs. L'exemple suivant est juste un exemple - l'écriture de l'analyseur csv avec regexps est une mauvaise idée, et vous ne devriez jamais le faire, mais je suis trop paresseux pour fournir l'analyseur de travail réel dans le cadre de l'exemple.

CREATE type parse_csv_srf as (catid INT4, whatever TEXT); 
CREATE OR REPLACE FUNCTION parse_csv(TEXT) RETURNS setof parse_csv_srf as $$ 
my $source = shift; 
my @rows = split /\r?\n/, $source; 
my @reply =(); 
for my $row (@rows) { 
    my @values =(); 
    while ($row =~ s/("(?:[^"]|"")*"|[^",]*)(,|$)//) { 
     my $single_value = $1; 
     $single_value =~ s/^"//; 
     $single_value =~ s/"$//; 
     $single_value =~ s/""/"/g; 
     push @values, $single_value; 
     last if '' eq $row; 
    } 
    push @reply, { 
     "catid" => $values[0], 
     "whatever" => $values[1], 
    }; 
} 
return \@reply; 
$$ language plperl; 

# select * from parse_csv(E'1,depesz\n2,"hubert lubaczewski"\n'); 
catid |  whatever 
-------+-------------------- 
    1 | depesz 
    2 | hubert lubaczewski 
(2 rows) 

# select i.*, c.relpages 
    from parse_csv(E'1,pg_database\n2,"pg_proc"\n') as i 
     join pg_class c on i.whatever = c.relname; 
catid | whatever | relpages 
-------+-------------+---------- 
    1 | pg_database |  1 
    2 | pg_proc  |  53 
(2 rows) 
Questions connexes