web-dev-qa-db-fra.com

Puis-je créer automatiquement une table dans PostgreSQL à partir d'un fichier csv avec des en-têtes?

J'exécute PostgreSQL 9.2.6 sur OS X 10.6.8. Je voudrais importer des données d'un fichier CSV avec des en-têtes de colonne dans une base de données. Je peux le faire avec l'instruction COPY, mais uniquement si je crée d'abord manuellement une table avec une colonne pour chaque colonne du fichier CSV. Existe-t-il un moyen de créer automatiquement ce tableau en fonction des en-têtes du fichier CSV?

Par cette question j'ai essayé

COPY test FROM '/path/to/test.csv' CSV HEADER;

Mais je reçois juste cette erreur:

ERROR: relation "test" does not exist

Et si je crée d'abord une table sans colonnes:

CREATE TABLE test ();

Je reçois:

ERROR: extra data after last expected column

Je ne trouve rien dans PostgreSQL documentation COPY sur la création automatique d'une table. Existe-t-il un autre moyen de créer automatiquement une table à partir d'un fichier CSV avec des en-têtes?

43
ihough

Vous ne trouvez rien dans la documentation COPY , car COPY ne peut pas créer une table pour vous.
Vous devez le faire avant de pouvoir COPY.

24
Erwin Brandstetter

Il existe un très bon outil qui importe des tables dans Postgres à partir d'un fichier csv. Il s'agit d'un outil en ligne de commande appelé pgfutter ( avec des binaires pour windows, linux, etc. ). L'un de ses grands avantages est qu'il reconnaît également les noms d'attribut/colonne.

L'utilisation de l'outil est simple. Par exemple, si vous souhaitez importer myCSVfile.csv:

pgfutter --db "myDatabase" --port "5432" --user "postgres" --pw "mySecretPassword" csv myCSVfile.csv

Cela va créer une table (appelée myCSVfile) avec les noms de colonne tirés de l'en-tête du fichier csv. De plus, les types de données seront identifiés à partir des données existantes.

Quelques notes: La commande pgfutter varie en fonction du binaire que vous utilisez, par ex. il pourrait être pgfutter_windows_AMD64.exe (renommez-le si vous avez l'intention d'utiliser cette commande fréquemment). La commande ci-dessus doit être exécutée dans une fenêtre de ligne de commande (par exemple, dans Windows, exécutez cmd et assurez-vous que pgfutter est accessible). Si vous souhaitez avoir un nom de table différent, ajoutez --table "myTable"; pour sélectionner un schéma de base de données particulier, nous --schema "mySchema". Si vous accédez à une base de données externe, utilisez --Host "myHostDomain".

Un exemple plus élaboré de pgfutter pour importer myFile dans myTable est celui-ci:

pgfutter --Host "localhost" --port "5432" --db "myDB" --schema "public" --table "myTable" --user "postgres" --pw "myPwd" csv myFile.csv

Vous changerez probablement quelques types de données (du texte au numérique) après l'importation:

alter table myTable
  alter column myColumn type numeric
    using (trim(myColumn)::numeric)
33
Wolfi

Il y a une deuxième approche, que j'ai trouvée ici (de mmatt). Fondamentalement, vous appelez une fonction dans Postgres (le dernier argument spécifie le nombre de colonnes).

select load_csv_file('myTable','C:/MyPath/MyFile.csv',24)

Voici le code de fonction de mmatt, que j'ai dû modifier légèrement, car je travaille sur le schéma public. (copiez et collez dans PgAdmin SQL Editor et exécutez-le pour créer la fonction)

CREATE OR REPLACE FUNCTION load_csv_file(
    target_table text,
    csv_path text,
    col_count integer)
  RETURNS void AS
$BODY$

declare

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

begin
    set schema 'public';

    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_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
    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;

$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION load_csv_file(text, text, integer)
  OWNER TO postgres;

Remarque: Il existe un problème courant lors de l'importation de fichiers texte liés à l'encodage. Le fichier csv doit être au format UTF-8. Cependant, parfois, cela n'est pas tout à fait atteint par les programmes, qui essaient de faire l'encodage. J'ai résolu ce problème en ouvrant le fichier dans Notepad ++ et en le convertissant en ANSI puis en UTF8.

15
Wolfi

Je l'ai atteint avec ces étapes:

  1. Convertissez le fichier csv en utf8
    iconv -f ISO-8859-1 -t UTF-8 file.txt -o file.csv
  1. Utilisez ce script python pour créer le sql pour créer une table et copier
#!/usr/bin/env python3
import csv, os
#pip install python-slugify
from slugify import slugify

origem = 'file.csv'
destino = 'file.sql'
arquivo = os.path.abspath(origem)

d = open(destino,'w')
with open(origem,'r') as f:

    header = f.readline().split(';')
    head_cells = []
    for cell in header:
        value = slugify(cell,separator="_")
        if value in head_cells:
            value = value+'_2'
        head_cells.append(value)
    #cabecalho = "{}\n".format(';'.join(campos))

    #print(cabecalho)
    fields= []
    for cell in head_cells:
        fields.append(" {} text".format(cell))
    table = origem.split('.')[0]
    sql = "create table {} ( \n {} \n);".format(origem.split('.')[0],",\n".join(fields))
    sql += "\n COPY {} FROM '{}' DELIMITER ';' CSV HEADER;".format(table,arquivo)

    print(sql)
    d.write(sql)

3.Exécutez le script avec

python3 importar.py

Facultatif: modifiez le script sql pour ajuster les types de champs (tous sont du texte par défaut)

  1. Exécutez le script sql. Abréviation de console
Sudo -H -u postgres bash -c "psql mydatabase < file.sql" 
0
Alexandre Andrade

Pour une seule table, je l'ai fait très simplement, rapidement et en ligne grâce à l'un des nombreux bons convertisseurs que l'on trouve sur le web. Il suffit de google convertir csv en sql en ligne et en choisir un.

0
Franco