Comment copier un fichier CSV vers une table PostgreSQL avec des en-têtes dans un fichier CSV?

93

Je souhaite copier un fichier CSV dans une table Postgres. Il y a environ 100 colonnes dans ce tableau, donc je ne veux pas les réécrire si je n'ai pas à le faire.

J'utilise la \copy table from 'table.csv' delimiter ',' csv;commande mais sans table créée je reçois ERROR: relation "table" does not exist. Si j'ajoute une table vide, je n'obtiens aucune erreur, mais rien ne se passe. J'ai essayé cette commande deux ou trois fois et il n'y avait pas de sortie ou de messages, mais le tableau n'était pas mis à jour lorsque je l'ai vérifié via PGAdmin.

Existe-t-il un moyen d'importer un tableau avec des en-têtes inclus comme j'essaie de le faire?

Coupe Stanley Phil
la source
2
Votre table est nommée table? Très perturbant. Le tableau existe-t-il ou souhaitez-vous le créer à partir du CSV? (vous ne pouvez pas)
wildplasser
1
Eh bien, je l'ai nommé autre chose, mais pour cet exemple, appelons-le table. J'ai essayé avec et sans qu'il existe, j'ai également essayé de le faire \copy table(column1, column2, ...) from 'table.csv' delimiter ',' csv;sans chance non plus. Idéalement, la table pourrait être créée uniquement via le CSV et utiliser les en-têtes de ce fichier.
Stanley Cup Phil
2
Juste un avertissement pour quiconque envisage de transformer un grand csv en une table postgres - postgres est plafonné à 1600 colonnes dans une seule table. Vous ne pouvez pas diviser les tables en tables de 1 600 colonnes, puis les joindre après. Vous devez repenser la base de données.
Achekroud
Si python est disponible pour vous, vous pouvez utiliser d6tstack . Il prend également en charge les changements de schéma.
citynorman

Réponses:

135

Cela a fonctionné. La première ligne contenait des noms de colonnes.

COPY wheat FROM 'wheat_crop_data.csv' DELIMITER ';' CSV HEADER
G. Cito
la source
5
Je pense que le problème avec cette commande est que vous devez être le superutilisateur DB. \ copy fonctionne aussi comme un utilisateur normal
Exocom
29
COPYne crée pas de tableau ni n'y ajoute de colonnes, il ajoute des lignes à une table existante avec ses colonnes existantes. Vraisemblablement, le demandeur veut automatiser la création des ~ 100 colonnes, et COPYn'a pas cette fonctionnalité, à partir de PG 9.3 au moins.
Daniel Vérité
2
@Exocom bonne prise. Comme je ne suis jamais un administrateur ou un super-utilisateur pour les bases de données sur les systèmes postgres que j'utilise (le pgadmin me rend propriétaire des bases de données que j'utilise et me donne des privilèges / rôles limités), j'ai dû utiliser `\ COPY '. Cheers
G. Cito
2
@Daniel J'ai compris que la table de l'utilisateur existait déjà et avait toutes les colonnes dont ils avaient besoin et qu'ils voulaient simplement des ADDdonnées.
G. Cito
Vous avez syntax error at or near "HEADER" LINE 2: delimiter ',' CSV HEADERsur aws redshift.
Mithril
24

Avec la bibliothèque Python pandas, vous pouvez facilement créer des noms de colonnes et déduire des types de données à partir d'un fichier csv.

from sqlalchemy import create_engine
import pandas as pd

engine = create_engine('postgresql://user:pass@localhost/db_name')
df = pd.read_csv('/path/to/csv_file')
df.to_sql('pandas_db', engine)

Le if_existsparamètre peut être défini pour remplacer ou ajouter à une table existante, par exemple df.to_sql('pandas_db', engine, if_exists='replace'). Cela fonctionne également pour les types de fichiers d'entrée supplémentaires, des documents ici et ici .

joelostblom
la source
1
Je trouve que pd.DataFrame.from_csv me pose moins de problèmes, mais cette réponse est de loin le moyen le plus simple de le faire, IMO.
brock
Certes, je ne sais pas pourquoi j'ai tapé pd.read_excelau lieu depd.read_csv . J'ai mis à jour la réponse.
joelostblom
1
c'est une solution fantastique lorsque vous ne voulez pas pré-créer la table qui contiendra un grand csv. Juste un avertissement cependant - postgres ne peut prendre que 1600 colonnes dans une table. Apparemment, d'autres moteurs DB en permettront plus. Avoir autant de colonnes est apparemment une mauvaise forme SQL, bien que ce consensus n'ait pas encore filtré jusqu'à l'épidémiologie.
Achekroud
1
Par défaut, il df.to_sql()est TRÈS LENT, pour accélérer cela, vous pouvez utiliser d6tstack . Il prend également en charge les changements de schéma.
citynorman
13

Alternative par terminal sans autorisation

La documentation pg à NOTES dit

Le chemin sera interprété par rapport au répertoire de travail du processus serveur (normalement le répertoire de données du cluster), et non au répertoire de travail du client.

Donc, généralement, en utilisant psqlou n'importe quel client, même dans un serveur local, vous avez des problèmes ... Et, si vous exprimez la commande COPY pour d'autres utilisateurs, par exemple. sur un README Github, le lecteur aura des problèmes ...

La seule façon d'exprimer le chemin relatif avec les autorisations du client consiste à utiliser STDIN ,

Lorsque STDIN ou STDOUT est spécifié, les données sont transmises via la connexion entre le client et le serveur.

comme rappelé ici :

psql -h remotehost -d remote_mydb -U myuser -c \
   "copy mytable (column1, column2) from STDIN with delimiter as ','" \
   < ./relative_path/file.csv
Peter Krauss
la source
3

J'utilise cette fonction depuis un moment sans problème. Il vous suffit de fournir le nombre de colonnes qu'il y a dans le fichier csv, et il prendra les noms d'en-tête de la première ligne et créera le tableau pour vous:

create or replace function data.load_csv_file
    (
        target_table  text, -- name of the table that will be created
        csv_file_path text,
        col_count     integer
    )

    returns void

as $$

declare
    iter      integer; -- dummy integer to iterate columns with
    col       text; -- to keep column names in each iteration
    col_first text; -- first column name, e.g., top left corner on a csv file or spreadsheet

begin
    set schema 'data';

    create table temp_table ();

    -- add just enough number of columns
    for iter in 1..col_count
    loop
        execute format ('alter table temp_table add column col_%s text;', iter);
    end loop;

    -- copy the data from csv file
    execute format ('copy temp_table from %L with delimiter '','' quote ''"'' csv ', csv_file_path);

    iter := 1;
    col_first := (select col_1
                  from temp_table
                  limit 1);

    -- update the column names based on the first row which has the column names
    for col in execute format ('select unnest(string_to_array(trim(temp_table::text, ''()''), '','')) from temp_table where col_1 = %L', col_first)
    loop
        execute format ('alter table temp_table rename column col_%s to %s', iter, col);
        iter := iter + 1;
    end loop;

    -- delete the columns row // using quote_ident or %I does not work here!?
    execute format ('delete from temp_table where %s = %L', col_first, col_first);

    -- change the temp table name to the name given as parameter, if not blank
    if length (target_table) > 0 then
        execute format ('alter table temp_table rename to %I', target_table);
    end if;
end;

$$ language plpgsql;
mehmet
la source
n'oubliez pas de changer set schema 'data';pour tout ce qui vous
convient
0

Vous pouvez utiliser d6tstack qui crée la table pour vous et est plus rapide que pd.to_sql () car il utilise des commandes d'importation DB natives. Il prend en charge Postgres ainsi que MYSQL et MS SQL.

import pandas as pd
df = pd.read_csv('table.csv')
uri_psql = 'postgresql+psycopg2://usr:pwd@localhost/db'
d6tstack.utils.pd_to_psql(df, uri_psql, 'table')

Il est également utile pour importer plusieurs CSV, résoudre les changements de schéma de données et / ou prétraiter avec des pandas (par exemple pour les dates) avant d'écrire dans db, voir plus bas dans les exemples de notebook

d6tstack.combine_csv.CombinerCSV(glob.glob('*.csv'), 
    apply_after_read=apply_fun).to_psql_combine(uri_psql, 'table')
citynorman
la source