¿Cómo copiar de un archivo CSV a una tabla PostgreSQL con encabezados en un archivo CSV?

93

Quiero copiar un archivo CSV a una tabla de Postgres. Hay alrededor de 100 columnas en esta tabla, por lo que no quiero reescribirlas si no es necesario.

Estoy usando el \copy table from 'table.csv' delimiter ',' csv;comando pero sin una tabla creada obtengo ERROR: relation "table" does not exist. Si agrego una tabla en blanco, no obtengo ningún error, pero no sucede nada. Probé este comando dos o tres veces y no hubo resultados ni mensajes, pero la tabla no se actualizó cuando la revisé a través de PGAdmin.

¿Hay alguna forma de importar una tabla con encabezados incluidos como estoy tratando de hacer?

Copa Stanley Phil
fuente
2
¿Tu mesa se llama table? Muy confuso. ¿Existe la tabla o desea crearla basándose en el CSV? (no puedes)
wildplasser
1
bueno, lo llamé de otra manera, pero para este ejemplo llamémoslo tabla. Intenté con y sin que existiera tampoco intenté hacerlo \copy table(column1, column2, ...) from 'table.csv' delimiter ',' csv;sin suerte. Idealmente, la tabla podría crearse solo a través del CSV y usar los encabezados en ese archivo.
Stanley Cup Phil
2
Solo un aviso para cualquiera que planee convertir un csv grande en una tabla postgres: postgres tiene un límite de 1600 columnas en una sola tabla. No puede dividir tablas en tablas del tamaño de 1600 columnas y luego unirlas después. Necesita rediseñar el archivo db.
Achekroud
Si Python está disponible para usted, puede usar d6tstack . También se ocupa de los cambios de esquema.
citynorman

Respuestas:

135

Esto funcionó. La primera fila tenía nombres de columnas.

COPY wheat FROM 'wheat_crop_data.csv' DELIMITER ';' CSV HEADER
G. Cito
fuente
5
Creo que el problema con este comando es que tienes que ser el superusuario de DB. \ copy también funciona como usuario normal
Exocom
29
COPYno crea una tabla ni le agrega columnas, agrega filas a una tabla existente con sus columnas existentes. Presumiblemente, el autor de la pregunta quiere automatizar la creación de las ~ 100 columnas y COPYno tiene esta funcionalidad, al menos a partir de PG 9.3.
Daniel Vérité
2
@Exocom buena captura. Como nunca soy administrador o superusuario de bases de datos en los sistemas de postgres que uso (el pgadmin me convierte en propietario de las bases de datos que uso y me otorga privilegios / roles limitados), debo haber usado '\ COPY'. Saludos
G. Cito
2
@Daniel Entendí que la tabla del usuario ya existía y tenía todas las columnas que necesitaban y que querían simplemente ADDdatos.
G. Cito
Tengo syntax error at or near "HEADER" LINE 2: delimiter ',' CSV HEADERun corrimiento al rojo aws.
Mithril
24

Con la biblioteca de Python pandas, puede crear fácilmente nombres de columna e inferir tipos de datos de un archivo 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_existsparámetro se puede configurar para reemplazar o agregar a una tabla existente, por ejemplo df.to_sql('pandas_db', engine, if_exists='replace'). Esto también funciona para tipos de archivos de entrada adicionales, documentos aquí y aquí .

joelostblom
fuente
1
Encuentro que pd.DataFrame.from_csv me da menos problemas, pero esta respuesta es, con mucho, la forma más fácil de hacer esto, en mi opinión.
brock
Es cierto, no estoy seguro de por qué escribí pd.read_excel, en lugar de pd.read_csv. Actualicé la respuesta.
joelostblom
1
esta es una solución fantástica para cuando no desea crear previamente la tabla que contendrá un csv grande. Sin embargo, solo un aviso: postgres solo puede tomar 1600 columnas en una tabla. Aparentemente, otros motores de base de datos permitirán más. Tener tantas columnas es aparentemente una forma de SQL pobre, aunque este consenso aún tiene que filtrarse a la epidemiología.
Achekroud
1
Por defecto df.to_sql()es MUY LENTO, para acelerar esto puedes usar d6tstack . También se ocupa de los cambios de esquema.
citynorman
13

Alternativa por terminal sin permiso

La documentación pg en NOTES dice

La ruta se interpretará en relación con el directorio de trabajo del proceso del servidor (normalmente el directorio de datos del clúster), no el directorio de trabajo del cliente.

Entonces, generalmente, usando psqlo cualquier cliente, incluso en un servidor local, tiene problemas ... Y, si está expresando el comando COPY para otros usuarios, por ejemplo. en un README de Github, el lector tendrá problemas ...

La única forma de expresar la ruta relativa con los permisos del cliente es utilizando STDIN ,

Cuando se especifica STDIN o STDOUT, los datos se transmiten a través de la conexión entre el cliente y el servidor.

como se recuerda aquí :

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

He estado usando esta función durante un tiempo sin problemas. Solo necesita proporcionar el número de columnas que hay en el archivo csv, y tomará los nombres de los encabezados de la primera fila y creará la tabla para usted:

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
fuente
no te olvides de cambiar set schema 'data';a lo que sea para ti
mehmet
0

Puede usar d6tstack que crea la tabla para usted y es más rápido que pd.to_sql () porque usa comandos nativos de importación de DB. Es compatible con Postgres, así como con MYSQL y 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')

También es útil para importar múltiples CSV, resolver cambios en el esquema de datos y / o preprocesar con pandas (por ejemplo, para fechas) antes de escribir en la base de datos, ver más abajo en el cuaderno de ejemplos.

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