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?
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
.
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)
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.
Je l'ai atteint avec ces étapes:
iconv -f ISO-8859-1 -t UTF-8 file.txt -o file.csv
#!/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)
Sudo -H -u postgres bash -c "psql mydatabase < file.sql"
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.