106 votes

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

Je veux copier un fichier CSV dans une table Postgres. Il y a environ 100 colonnes dans cette table, donc je ne veux pas les réécrire si je ne dois pas le faire.

J'utilise le \copy table from 'table.csv' delimiter ',' csv; mais sans qu'une table soit créée, j'obtiens ERROR: relation "table" does not exist . Si j'ajoute une table vierge, je n'obtiens aucune erreur, mais rien ne se produit. J'ai essayé cette commande deux ou trois fois et il n'y a pas eu de sortie ou de messages, mais la table n'a pas été mise à jour lorsque je l'ai vérifiée via PGAdmin.

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

148voto

G. Cito Points 1547

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

COPY wheat FROM 'wheat_crop_data.csv' DELIMITER ';' CSV HEADER

27voto

joelostblom Points 7189

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)

El if_exists peut être défini pour remplacer ou ajouter à une table existante, par ex. df.to_sql('pandas_db', engine, if_exists='replace') . Cela fonctionne aussi pour d'autres types de fichiers d'entrée, docs aquí y aquí .

13voto

Peter Krauss Points 1888

Alternative par terminal sans autorisation

El pg documentation à NOTES dites

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 par rapport au répertoire de travail du client.

Donc, en général, en utilisant psql ou tout autre 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 dans un README de Github, le lecteur aura des problèmes...

La seule façon d'exprimer chemin relatif avec les permissions du client utilise STDIN ,

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

comme rappelés ici :

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

4voto

mehmet Points 3219

J'utilise cette fonction depuis un certain temps sans aucun problème. Il vous suffit de fournir le nombre de colonnes du 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;

0voto

yurenchen Points 498
## csv with header
$ psql -U$db_user -h$db_host -p$db_port -d DB_NAME \
  -c "\COPY TB_NAME FROM 'data_sample.csv' WITH (FORMAT CSV, header);"

## csv without header
$ psql -U$db_user -h$db_host -p$db_port -d DB_NAME \
  -c "\COPY TB_NAME FROM 'data_sample.csv' WITH (FORMAT CSV);"

## csv without header, specify column
$ psql -U$db_user -h$db_host -p$db_port -d DB_NAME \
  -c "\COPY TB_NAME(COL1,COL2) FROM 'data_sample.csv' WITH (FORMAT CSV);"

toutes les colonnes du csv doivent être identiques à celles de la table (ou à la colonne spécifiée).

à propos de COPY
https://www.postgresql.org/docs/9.2/sql-copy.html

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X