Como copiar do arquivo CSV para a tabela PostgreSQL com cabeçalhos no arquivo CSV?

93

Quero copiar um arquivo CSV para uma tabela Postgres. Existem cerca de 100 colunas nesta tabela, então não quero reescrevê-las se não for necessário.

Estou usando o \copy table from 'table.csv' delimiter ',' csv;comando, mas sem uma tabela criada eu consigo ERROR: relation "table" does not exist. Se eu adicionar uma tabela em branco, não recebo nenhum erro, mas nada acontece. Tentei este comando duas ou três vezes e não houve saída ou mensagens, mas a tabela não foi atualizada quando a verifiquei através do PGAdmin.

Existe uma maneira de importar uma tabela com cabeçalhos incluídos como estou tentando fazer?

Stanley Cup Phil
fonte
2
Sua mesa tem nome table? Muito confuso. A tabela existe ou deseja criá-la com base no CSV? (você não pode)
wildplasser
1
bem, eu chamei de outra coisa, mas neste exemplo vamos chamá-lo de tabela. Eu tentei com e sem ele existir Eu também tentei fazer \copy table(column1, column2, ...) from 'table.csv' delimiter ',' csv;sem sorte também. Idealmente, a tabela poderia ser criada apenas por meio do CSV e usar os cabeçalhos desse arquivo.
Stanley Cup Phil
2
Apenas um aviso para quem está planejando transformar um grande csv em uma tabela postgres - o postgres é limitado a 1600 colunas em uma única tabela. Você não pode agrupar tabelas em tabelas com tamanho de 1600 colunas e juntá-las depois. Você precisa redesenhar o banco de dados.
Achekroud de
Se python estiver disponível para você, você pode usar d6tstack . Ele também cuida das mudanças de esquema.
citynorman

Respostas:

135

Isso funcionou. A primeira linha continha nomes de colunas.

COPY wheat FROM 'wheat_crop_data.csv' DELIMITER ';' CSV HEADER
G. Cito
fonte
5
Acho que o problema com esse comando é que você precisa ser o superusuário do DB. \ copy funciona como usuário normal também
Exocom de
29
COPYnão cria uma tabela ou adiciona colunas a ela, ele adiciona linhas a uma tabela existente com suas colunas existentes. Presumivelmente, o solicitante deseja automatizar a criação de ~ 100 colunas e COPYnão tem essa funcionalidade, pelo menos a partir do PG 9.3.
Daniel Vérité
2
@Exocom good catch. Visto que nunca sou um administrador ou superusuário para bancos de dados nos sistemas postgres que uso (o pgadmin me torna o proprietário dos bancos de dados que uso e me dá privilégios / funções limitados), devo ter usado `\ COPY '. Saúde
G. Cito
2
@Daniel Eu entendi que a tabela do usuário já existia e tinha todas as colunas que eles precisavam e que eles queriam simplesmente ADDdados.
G. Cito
Começou syntax error at or near "HEADER" LINE 2: delimiter ',' CSV HEADERem aws redshift.
Mithril
24

Com a biblioteca Python pandas, você pode criar facilmente nomes de coluna e inferir tipos de dados de um arquivo 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)

O if_existsparâmetro pode ser definido para substituir ou anexar a uma tabela existente, por exemplo df.to_sql('pandas_db', engine, if_exists='replace'). Isso também funciona para tipos de arquivo de entrada adicionais, documentos aqui e aqui .

joelostblom
fonte
1
Acho que pd.DataFrame.from_csv me dá menos problemas, mas esta resposta é de longe a maneira mais fácil de fazer isso, IMO.
brock
É verdade, não sei por que digitei pd.read_excel, em vez de pd.read_csv. Eu atualizei a resposta.
joelostblom,
1
esta é uma solução fantástica para quando você não quiser pré-criar a tabela que conterá um csv grande. Só um aviso - o postgres só pode ter 1600 colunas em uma tabela. Aparentemente, outros motores de banco de dados permitirão mais. Ter tantas colunas é, aparentemente, uma forma SQL pobre, embora esse consenso ainda não tenha chegado à epidemiologia.
Achekroud de
1
Por padrão, df.to_sql()é MUITO LENTO, para acelerar isso você pode usar d6tstack . Ele também cuida das mudanças de esquema.
citynorman
13

Alternativa por terminal sem permissão

A documentação da página em NOTES diz

O caminho será interpretado em relação ao diretório de trabalho do processo do servidor (normalmente o diretório de dados do cluster), não ao diretório de trabalho do cliente.

Então, geralmente, usando psqlou qualquer cliente, mesmo em um servidor local, você tem problemas ... E, se você está expressando comando COPY para outros usuários, por exemplo. em um README do Github, o leitor terá problemas ...

A única maneira de expressar caminho relativo com as permissões do cliente é usando STDIN ,

Quando STDIN ou STDOUT é especificado, os dados são transmitidos por meio da conexão entre o cliente e o servidor.

como lembrado aqui :

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

Eu uso esta função há algum tempo sem problemas. Você só precisa fornecer as colunas numéricas que existem no arquivo csv, e ele pegará os nomes dos cabeçalhos da primeira linha e criará a tabela para você:

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
fonte
não se esqueça de mudar set schema 'data';para o que for seu caso
mehmet
0

Você pode usar o d6tstack, que cria a tabela para você e é mais rápido do que pd.to_sql () porque usa comandos de importação de banco de dados nativos. Suporta Postgres, MYSQL e 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')

Também é útil para importar vários CSVs, resolver alterações de esquema de dados e / ou pré-processar com pandas (por exemplo, para datas) antes de gravar no banco de dados, veja mais abaixo no caderno de exemplos

d6tstack.combine_csv.CombinerCSV(glob.glob('*.csv'), 
    apply_after_read=apply_fun).to_psql_combine(uri_psql, 'table')
Citynorman
fonte