2016-06-03 1 views
2

J'ai un ensemble de données dans un fichier CSV composé de 2500 lignes. Le fichier est structuré (simplifié) façon:Alternative à "écrire dans un fichier" pour transférer des données CSV à PostgreSQL en utilisant COPY pour de meilleures performances?

id_run; run_name; receptor1; receptor2; receptor3_value; [...]; receptor50_value

Chaque récepteur du fichier est déjà dans une table et un identifiant unique.

J'ai besoin de télécharger chaque ligne à une table avec ce format:

id_run; id_receptor; receptor_value 
1; 1; 2.5 
1; 2; 3.2 
1; 3, 2.1 
[...] 
2500, 1, 2.4 
2500, 2, 3.0 
2500, 3, 1.1 

En fait, j'écris toutes les données que je dois télécharger dans un fichier txt et j'utilise la commande COPY à partir de postgreSQL pour transférer le fichier dans la table de destination.

Pour 2500 exécutions (donc 2500 lignes dans le fichier CSV) et 50 récepteurs, mon programme Python génère ~ 110000 enregistrements dans le fichier texte à télécharger.

Je supprime les clés étrangères de la table de destination et les restaure après le téléchargement.

En utilisant cette méthode, il faut environ ~ 8 secondes pour générer le fichier texte et 1 seconde pour copier le fichier dans la table.

Existe-t-il un moyen, une méthode, une bibliothèque ou tout autre moyen que je pourrais utiliser pour accélérer la préparation des données afin que 90% du temps requis ne soit pas consacré à l'écriture du fichier texte?

Edit: Code

Voici mon (mis à jour). J'utilise maintenant une écriture en bloc dans le fichier texte. Il semble l'aimer plus vite (téléchargé 110 000 lignes en 3,8 secondes).

# Bulk write to file 
lines = [] 
for line_i, line in enumerate(run_specs): 
    # the run_specs variable consists of the attributes defining a run 
    # (id_run, run_name, etc.). So basically a line in the CSV file without the 
    # receptors data 
    sc_uid = get_uid(db, table_name) # function to get the unique ID of the run 
    for rec_i, rec in enumerate(rec_uids): 
     # the rec_uids variable is the unique IDs in the database for the 
     # receptors in the CSV file 
     line_to_write = '%s %s %s\n' % (sc_uid, rec, rec_values[line_i][rec_i]) 
     lines.append(line_to_write) 

# write to file 
fn = r"data\tmp_data_bulk.txt" 
with open(fn, 'w') as tmp_data: 
    tmp_data.writelines(lines) 

# get foreign keys of receptor_results 
rr_fks = DB.get_fks(conn, 'receptor_results') # function to get foreign keys 

# drop the foreign keys 
for key in rr_fks: 
    DB.drop_fk(conn, 'receptor_results', key[0]) # funciton to drop FKs 

# upload data with custom function using the COPY SQL command 
DB.copy_from(conn, fn, 'receptor_results', ['sc_uid', 'rec_uid', 'value'],\ 
                    " ", False) 

# restore foreign keys 
for key in rr_fks: 
    DB.create_fk(conn, 'receptor_results', key[0], key[1], key[2]) 

# commit to database 
conn.commit() 

Edit # 2:

Utilisation de la bibliothèque cStringIO, j'ai remplacé la création d'un fichier texte temporaire avec un objet type fichier, mais les gains de vitesse est très très faible.

code modifié:

outf = cStringIO.StringIO() 
for rec_i, rec in enumerate(rec_uids): 
    outf.write('%s %s %s\n' % (sc_uid, rec, rec_values[line_i][rec_i])) 

cur.copy_from(outf, 'receptor_results') 
+1

Quel code utilisez-vous actuellement? [mcve] –

+0

J'ai édité le post principal avec mon code! – kaycee

+0

Si votre question est sur la façon de préparer vos données texte rapidement, alors il n'a rien à voir avec le serveur de base de données. Ceci est déroutant. –

Répondre

1

J'écris toutes les données que je dois télécharger dans un fichier txt et je suis en utilisant la commande Copier postgreSQL pour transférer le fichier à la table de destination .

Il est un aller-retour lourd et inutile pour toutes vos données. Puisque vous avez déjà en mémoire, vous devez simplement le traduire en une insertion multiple directement:

INSERT INTO table(col1, col2) VALUES (val1, val2), (val3, val4), ... 

dire concaténer vos données dans une telle requête et exécuter tel quel.

Dans votre cas, vous générez et exécutez probablement 50 de ces insertions, avec 2500 lignes dans chacune d'elles, selon vos besoins.

Ce sera la solution la plus performante;)

1

Oui, il y a quelque chose que vous pouvez faire pour accélérer l'écriture des données dans le fichier à l'avance: ne prenez pas la peine!

Vous avez déjà ajusté les données en mémoire, ce n'est donc pas un problème. Ainsi, au lieu d'écrire les lignes dans une liste de chaînes, écrivez-les dans un objet légèrement différent - une instance StringIO. Ensuite, les données peuvent rester en mémoire et servir de paramètre à la fonction copy_from de psycopg2.

filelike = StringIO.StringIO('\n'.join(['1\tA', '2\tB', '3\tC'])) 
cursor.copy_from(filelike, 'your-table-name') 

Notez que le StringIO doit contenir les nouvelles lignes, les séparateurs de champs et ainsi de suite - tout comme le fichier aurait.

+0

J'ai utilisé la bibliothèque cStringIO, mais le gain de vitesse est très minime. Voir Edit # 2 dans le message principal. – kaycee