我可以从带有标题的 csv 文件在 PostgreSQL 中自动创建一个表吗?
Posted
技术标签:
【中文标题】我可以从带有标题的 csv 文件在 PostgreSQL 中自动创建一个表吗?【英文标题】:Can I automatically create a table in PostgreSQL from a csv file with headers? 【发布时间】:2014-01-27 21:18:33 【问题描述】:我在 OS X 10.6.8 上运行 PostgreSQL 9.2.6。我想将带有列标题的 CSV 文件中的数据导入数据库。我可以使用COPY
语句来做到这一点,但前提是我首先手动创建一个表,其中包含 CSV 文件中的每一列的列。有什么方法可以根据 CSV 文件中的表头自动创建这个表?
每个this question我都试过了
COPY test FROM '/path/to/test.csv' CSV HEADER;
但我只是得到这个错误:
ERROR: relation "test" does not exist
如果我首先创建一个没有列的表:
CREATE TABLE test ();
我明白了:
ERROR: extra data after last expected column
我在 PostgreSQL COPY documentation 中找不到任何关于自动创建表的信息。有没有其他方法可以从带有标题的 CSV 文件自动创建表格?
【问题讨论】:
【参考方案1】:您在 COPY
文档中找不到任何内容,因为 COPY 无法为您创建表。
你需要这样做才能COPY
。
【讨论】:
这个答案其实并没有回答他的问题? 这个答案应该是评论,不能被接受,因为它没有回答问题。【参考方案2】:有一个非常好的工具可以将表格从 csv 文件导入 Postgres。 它是一个名为 pgfutter (with binaries for windows, linux, etc.) 的命令行工具。它的一大优点是它也可以识别属性/列名称。
该工具的使用很简单。例如,如果您想导入myCSVfile.csv
:
pgfutter --db "myDatabase" --port "5432" --user "postgres" --pw "mySecretPassword" csv myCSVfile.csv
这将创建一个表(称为myCSVfile
),其中列名取自 csv 文件的标题。此外,将从现有数据中识别数据类型。
一些注意事项:命令pgfutter
取决于您使用的二进制文件,例如它可能是pgfutter_windows_amd64.exe
(如果您打算经常使用此命令,请重命名)。上述命令必须在命令行窗口中执行(例如,在 Windows 中运行 cmd
并确保 pgfutter
可访问)。如果您想使用不同的表名,请添加--table "myTable"
;选择一个特定的数据库模式我们--schema "mySchema"
。如果您正在访问外部数据库,请使用--host "myHostDomain"
。
将myFile
导入myTable
的pgfutter
的更详细示例是这个:
pgfutter --host "localhost" --port "5432" --db "myDB" --schema "public" --table "myTable" --user "postgres" --pw "myPwd" csv myFile.csv
您很可能会在导入后更改一些数据类型(从文本到数字):
alter table myTable
alter column myColumn type numeric
using (trim(myColumn)::numeric)
【讨论】:
如果我指定模式名称,我需要创建目标表和列。令人沮丧 当您尝试导入 CSV 文件时,此工具目前因“索引超出范围”异常而失败。 没有更改分隔符的选项。 :-( 看来您现在可以更改分隔符,例如使用制表符:pgfutter csv -d $'\t' traffic_violations.csv
。
目前这个应用程序在 CentOS 8 下对我“正常工作”。【参考方案3】:
还有第二种方法,我找到了here(来自 mmatt)。基本上你在 Postgres 中调用一个函数(最后一个参数指定列数)。
select load_csv_file('myTable','C:/MyPath/MyFile.csv',24)
这里是 mmatt 的功能代码,我不得不稍微修改一下,因为我正在处理公共模式。 (复制并粘贴到 PgAdmin SQL 编辑器并运行它以创建函数)
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;
注意:导入与编码相关的文本文件存在一个常见问题。 csv 文件应为 UTF-8 格式。但是,有时尝试进行编码的程序并不能完全实现这一点。我通过在 Notepad++ 中打开文件并将其转换为 ANSI 并转换回 UTF8 解决了这个问题。
【讨论】:
这是一个整洁的解决方案。注意确保所有列都没有保留关键字名称 - 例如,如果 CSV 文件有一个名为order
的列(例如订单号),请将其更改为 order_num
。另外,请记住根据需要更改列TYPE
。
此解决方案失败,因为它尝试在服务器中打开 CSV 文件,而不是在客户端中。您需要访问服务器的文件系统才能使这样的事情起作用。
我们如何让它使用本地机器上的 CSV 文件?我相信它正在寻找服务器上的文件。错误:必须是超级用户或 pg_read_server_files 角色的成员才能从文件复制提示:任何人都可以复制到标准输出或标准输入。 psql 的 \copy 命令也适用于任何人。【参考方案4】:
对于单个表,我通过网络上可以找到的众多优秀转换器之一进行了非常简单、快速和在线的操作。 只需谷歌convert csv to sql online 并选择一个。
【讨论】:
【参考方案5】:我通过以下步骤实现了它:
-
将 csv 文件转换为 utf8
iconv -f ISO-8859-1 -t UTF-8 file.txt -o file.csv
-
使用这个python脚本创建sql来创建表和复制
#!/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.运行脚本
python3 importar.py
可选:编辑sql脚本调整字段类型(默认均为文本)
-
运行 sql 脚本。控制台的缩写
sudo -H -u postgres bash -c "psql mydatabase < file.sql"
【讨论】:
【参考方案6】:我正在使用csvsql
生成表格布局(它会自动猜测格式):
head -n 20 table.csv | csvsql --no-constraints --tables table_name
然后我在psql
中使用\COPY
。这对我来说是导入 CSV 文件的最快方法。
您还可以使用sed
和csvsql
以获得所需的数据类型:
head -n 20 table.csv | csvsql --no-constraints --tables table_name | sed 's/DECIMAL/NUMERIC/' | sed 's/VARCHAR/TEXT/'
【讨论】:
【参考方案7】:我没用过,但是 pgfutter 开发者推荐 pgLoader (https://pgloader.io/) 来解决更复杂的问题(见上面的答案)。看起来很能干。
【讨论】:
我尝试使用它,但找不到一种方法让它为您创建一个带有 csv 输入的表。 (看来还是需要写create table语句) 是的,它看起来像you're right,它需要create table
语句;我没有注意到这一点。我想主要用于更复杂的情况,例如加载期间的动态转换。【参考方案8】:
使用 sqlite 作为中间步骤。
步骤:
-
在命令提示符下输入:
sqlite3
在 sqlite3 CLI 中输入:.mode csv
.import my_csv.csv my_table
.output my_table_sql.sql
.dump my_table
最后在你的 Postgresql 中执行那个 sql
【讨论】:
TIL sqlite 有很好的导入工具!谢谢这对我很有用。【参考方案9】:您可以使用 CSV 在DBeaver 中创建一个新表。
【讨论】:
以上是关于我可以从带有标题的 csv 文件在 PostgreSQL 中自动创建一个表吗?的主要内容,如果未能解决你的问题,请参考以下文章