Jak skopiować z pliku CSV do tabeli PostgreSQL z nagłówkami w pliku CSV?

93

Chcę skopiować plik CSV do tabeli Postgres. W tej tabeli jest około 100 kolumn, więc nie chcę ich przepisywać, jeśli nie muszę.

Używam \copy table from 'table.csv' delimiter ',' csv;polecenia, ale bez utworzonej tabeli otrzymuję ERROR: relation "table" does not exist. Jeśli dodam pustą tabelę, nie pojawia się żaden błąd, ale nic się nie dzieje. Próbowałem tego polecenia dwa lub trzy razy i nie było żadnych danych wyjściowych ani komunikatów, ale tabela nie została zaktualizowana, gdy sprawdzałem ją przez PGAdmin.

Czy istnieje sposób zaimportowania tabeli z dołączonymi nagłówkami, tak jak próbuję to zrobić?

Puchar Stanleya Phil
źródło
2
Twój stół ma nazwę table? Bardzo mylące. Czy tabela istnieje, czy chcesz ją utworzyć na podstawie pliku CSV? (nie możesz)
wildplasser
1
cóż, nazwałem to inaczej, ale w tym przykładzie nazwijmy to table. Próbowałem z istniejącym i bez niego, próbowałem też zrobić \copy table(column1, column2, ...) from 'table.csv' delimiter ',' csv;bez powodzenia. W idealnym przypadku tabelę można by utworzyć za pomocą samego pliku CSV i użyć nagłówków w tym pliku.
Puchar Stanleya Phil
2
Tylko uwaga dla każdego, kto planuje zamienić duży plik csv w tabelę postgres - liczba postgres jest ograniczona do 1600 kolumn w jednej tabeli. Nie można podzielić tabel na tabele o wielkości 1600 kolumn, a następnie dołączyć do nich. Musisz przeprojektować db.
Achekroud
Jeśli Python jest dla Ciebie dostępny, możesz użyć d6tstack . Dba również o zmiany schematów.
citynorman

Odpowiedzi:

135

To zadziałało. Pierwszy wiersz zawierał nazwy kolumn.

COPY wheat FROM 'wheat_crop_data.csv' DELIMITER ';' CSV HEADER
G. Cito
źródło
5
Myślę, że problem z tym poleceniem polega na tym, że musisz być superużytkownikiem DB. \ copy działa również jako zwykły użytkownik
Exocom,
29
COPYnie tworzy tabeli ani nie dodaje do niej kolumn, dodaje wiersze do istniejącej tabeli z istniejącymi kolumnami. Przypuszczalnie pytający chce zautomatyzować tworzenie ~ 100 kolumn i COPYnie ma takiej funkcjonalności, przynajmniej od wersji PG 9.3.
Daniel Vérité
2
@Exocom dobry chwyt. Ponieważ nigdy nie jestem administratorem ani superużytkownikiem DB w systemach postgres, których używam (pgadmin czyni mnie właścicielem baz danych, z których korzystam i daje mi ograniczone uprawnienia / role), musiałem użyć `\ COPY '. Pozdrawiam
G. Cito
2
@Daniel Zrozumiałem, że tabela użytkownika już istnieje i ma wszystkie potrzebne kolumny, a oni chcą po prostu ADDdane.
G. Cito
Masz syntax error at or near "HEADER" LINE 2: delimiter ',' CSV HEADERaws przesunięcie ku czerwieni.
Mithril
24

Dzięki bibliotece Python pandasmożesz łatwo tworzyć nazwy kolumn i wywnioskować typy danych z pliku 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)

if_existsParametr można ustawić, aby zastąpić lub dołączyć do istniejącej tabeli, np df.to_sql('pandas_db', engine, if_exists='replace'). Działa to również w przypadku dodatkowych typów plików wejściowych, dokumenty tutaj i tutaj .

joelostblom
źródło
1
Uważam, że pd.DataFrame.from_csv sprawia mi mniej kłopotów, ale ta odpowiedź jest zdecydowanie najłatwiejszym sposobem, aby to zrobić, IMO.
brock
To prawda, nie jestem pewien, dlaczego pd.read_excelzamiast tego wpisałem pd.read_csv. Zaktualizowałem odpowiedź.
joelostblom
1
jest to fantastyczne rozwiązanie, gdy nie chcesz wstępnie tworzyć tabeli, która pomieści duży plik csv. Tylko jedno ostrzeżenie - postgres może zająć tylko 1600 kolumn w tabeli. Najwyraźniej inne silniki DB pozwolą na więcej. Posiadanie tak wielu kolumn jest najwyraźniej kiepską formą SQL, chociaż ten konsensus jeszcze nie dotarł do epidemiologii.
Achekroud
1
Domyślnie df.to_sql()jest BARDZO WOLNE, aby to przyspieszyć, możesz użyć d6tstack . Dba również o zmiany schematów.
citynorman
13

Alternatywnie przez terminal bez pozwolenia

Dokumentacja pg w NOTES say

Ścieżka zostanie zinterpretowana względem katalogu roboczego procesu serwera (zwykle katalogu danych klastra), a nie katalogu roboczego klienta.

Więc gerally, używając psqllub dowolnego klienta, nawet na lokalnym serwerze, masz problemy ... A jeśli używasz polecenia COPY dla innych użytkowników, np. w Github README czytelnik będzie miał problemy ...

Jedynym sposobem wyrażenia ścieżki względnej z uprawnieniami klienta jest użycie STDIN ,

Jeśli określono STDIN lub STDOUT, dane są przesyłane za pośrednictwem połączenia między klientem a serwerem.

jak zapamiętano tutaj :

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

Używam tej funkcji od jakiegoś czasu bez żadnych problemów. Wystarczy podać liczbę kolumn znajdujących się w pliku csv, a nazwy nagłówków z pierwszego wiersza zostaną utworzone dla Ciebie:

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
źródło
nie zapomnij zmienić set schema 'data';tego, co jest dla Ciebie
mehmet
0

Możesz użyć d6tstack który tworzy tabelę za Ciebie i jest szybszy niż pd.to_sql (), ponieważ używa natywnych poleceń importu DB. Obsługuje Postgres, a także MYSQL i 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')

Jest również przydatny do importowania wielu plików CSV, rozwiązywania zmian w schemacie danych i / lub przetwarzania wstępnego za pomocą pand (np. Dat) przed zapisaniem do bazy danych, patrz dalej w przykładach notatnik

d6tstack.combine_csv.CombinerCSV(glob.glob('*.csv'), 
    apply_after_read=apply_fun).to_psql_combine(uri_psql, 'table')
citynorman
źródło