如何编写从CSV文件导入数据并填充表的存储过程?


当前回答

一种快速的方法是使用Python Pandas库(0.15或更高版本最好)。这将为您处理创建列的问题——尽管它为数据类型所做的选择可能不是您想要的。如果它不能完全做到你想要的,你总是可以使用生成为模板的“创建表”代码。

这里有一个简单的例子:

import pandas as pd
df = pd.read_csv('mypath.csv')
df.columns = [c.lower() for c in df.columns] # PostgreSQL doesn't like capitals or spaces

from sqlalchemy import create_engine
engine = create_engine('postgresql://username:password@localhost:5432/dbname')

df.to_sql("my_table_name", engine)

下面是一些代码,告诉你如何设置各种选项:

# Set it so the raw SQL output is logged
import logging
logging.basicConfig()
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)

df.to_sql("my_table_name2",
          engine,
          if_exists="append",  # Options are ‘fail’, ‘replace’, ‘append’, default ‘fail’
          index = False, # Do not output the index of the dataframe
          dtype = {'col1': sqlalchemy.types.NUMERIC,
                   'col2': sqlalchemy.types.String}) # Datatypes should be SQLAlchemy types

其他回答

这里的大多数其他解决方案都要求您提前/手动创建表。这在某些情况下可能不实用(例如,如果目标表中有很多列)。因此,下面的方法可能会派上用场。

提供你的CSV文件的路径和列数,你可以使用下面的函数来加载你的表到一个临时表,它将被命名为target_table:

假设第一行具有列名。

create or replace function data.load_csv_file
(
    target_table text,
    csv_path text,
    col_count integer
)

returns void as $$

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

$$ language plpgsql;

如果你没有权限使用COPY(在db服务器上工作),你可以使用\ COPY(在db客户端上工作)。以Bozhidar Batsov为例:

创建你的表:

CREATE TABLE zip_codes
(ZIP char(5), LATITUDE double precision, LONGITUDE double precision,
CITY varchar, STATE char(2), COUNTY varchar, ZIP_CLASS varchar);

将数据从CSV文件复制到表中:

\copy zip_codes FROM '/path/to/csv/ZIP_CODES.txt' DELIMITER ',' CSV

注意那个\拷贝…必须用一行写,不带下划线;最后!

你也可以指定要读取的列:

\copy zip_codes(ZIP,CITY,STATE) FROM '/path/to/csv/ZIP_CODES.txt' DELIMITER ',' CSV

参见COPY的文档:

不要将COPY与psql指令\ COPY混淆。\copy调用copy FROM STDIN或copy TO STDOUT,然后在psql客户端可访问的文件中获取/存储数据。因此,当使用\copy时,文件的可访问性和访问权限取决于客户端而不是服务器。

并注意:

对于标识列,COPY FROM命令将始终写入输入数据中提供的列值,就像INSERT选项覆盖SYSTEM VALUE一样。

您可以创建一个Bash文件import.sh(您的CSV格式是一个制表符分隔符):

#!/usr/bin/env bash

USER="test"
DB="postgres"
TBALE_NAME="user"
CSV_DIR="$(pwd)/csv"
FILE_NAME="user.txt"

echo $(psql -d $DB -U $USER  -c "\copy $TBALE_NAME from '$CSV_DIR/$FILE_NAME' DELIMITER E'\t' csv" 2>&1 |tee /dev/tty)

然后运行这个脚本。

你有3个选项来导入CSV文件到PostgreSQL: 首先,通过命令行使用COPY命令。

其次,使用pgAdmin工具的导入/导出。

第三,使用像Skyvia这样的云解决方案,从在线位置(如FTP源)或云存储(如谷歌驱动器)获取CSV文件。

你可以从这里查看解释所有这些的文章。

在Python中,你可以使用这段代码自动创建带有列名的PostgreSQL表:

import pandas, csv

from io import StringIO
from sqlalchemy import create_engine

def psql_insert_copy(table, conn, keys, data_iter):
    dbapi_conn = conn.connection
    with dbapi_conn.cursor() as cur:
        s_buf = StringIO()
        writer = csv.writer(s_buf)
        writer.writerows(data_iter)
        s_buf.seek(0)
        columns = ', '.join('"{}"'.format(k) for k in keys)
        if table.schema:
            table_name = '{}.{}'.format(table.schema, table.name)
        else:
            table_name = table.name
        sql = 'COPY {} ({}) FROM STDIN WITH CSV'.format(table_name, columns)
        cur.copy_expert(sql=sql, file=s_buf)

engine = create_engine('postgresql://user:password@localhost:5432/my_db')

df = pandas.read_csv("my.csv")
df.to_sql('my_table', engine, schema='my_schema', method=psql_insert_copy)

它的速度也相对较快。我可以在大约4分钟内导入330多万行。