我有一个包含 10 列的 CSV 文件。创建具有 4 列的 PostgreSQL 表后,我想将 10 列中的一些复制到表中。
我的 CSV 表的列如下:
x1 x2 x3 x4 x5 x6 x7 x8 x9 x10
我的 PostgreSQL 表的列应该是这样的:
x2 x5 x7 x10
我有一个包含 10 列的 CSV 文件。创建具有 4 列的 PostgreSQL 表后,我想将 10 列中的一些复制到表中。
我的 CSV 表的列如下:
x1 x2 x3 x4 x5 x6 x7 x8 x9 x10
我的 PostgreSQL 表的列应该是这样的:
x2 x5 x7 x10
使用输入文件中的所有列创建一个临时表
create temporary table t (x1 integer, ... , x10 text)
从文件中复制到其中:
copy t (x1, ... , x10)
from '/path/to/my_file'
with (format csv)
现在从 temp 插入最终表:
insert into my_table (x2, x5, x7, x10)
select x2, x5, x7, x10
from t
放下它:
drop table t
使用file_fdw
扩展名。作为超级用户:
create extension file_fdw;
create server my_csv foreign data wrapper file_fdw;
create foreign table my_csv (
x1 integer,
x2 text,
x3 text
) server my_csv
options (filename '/tmp/my_csv.csv', format 'csv' )
;
将 table 的 select 权限授予将要读取它的用户:
grant select on table my_csv to the_read_user;
然后在必要时直接从 csv 文件中读取,就好像它是一个表一样:
insert into my_table (x2)
select x2
from my_csv
where x1 = 2
You can provide the columns your want to fill with the COPY
command. Like so:
\copy your_table (x2,x5,x7,x10) FROM '/path/to/your-file.csv' DELIMITER ',' CSV;
Here's the doc for the COPY
command.
正如其他答案所指出的那样,可以指定要复制到 PG 表中的列。但是,如果没有在 CSV 中引用列名的选项,除了加载到列具有不同顺序的表中之外,它几乎没有用处。
幸运的是,从 Postgres 9.3 开始,不仅可以从文件或标准输入复制列,还可以使用 PROGRAM 从 shell 命令复制列:
程序
要执行的命令。在 COPY FROM 中,输入从命令的标准输出中读取,而在 COPY TO 中,输出被写入命令的标准输入。
请注意,该命令由 shell 调用,因此如果您需要将任何来自不受信任来源的参数传递给 shell 命令,则必须小心去除或转义任何可能对 shell 具有特殊含义的特殊字符。出于安全原因,最好使用固定的命令字符串,或者至少避免在其中传递任何用户输入。
这是我们急切期待的功能所需的缺失部分。例如,我们可以将此选项与cut
(在基于 UNIX 的系统中)结合使用以按顺序选择某些列:
COPY my_table (x2, x5, x7, x10) FROM PROGRAM 'cut -d "," -f 2,5,7,10 /path/to/file.csv' WITH (FORMAT CSV, HEADER)
但是,cut
在操作 CSV 时有几个限制:它不能充分地操作带有逗号(或其他分隔符)的字符串,并且不允许按名称选择列。
还有其他几个开源命令行工具可以更好地处理 CSV 文件,例如csvkit或miller。这是一个使用miller
按名称选择列的示例:
COPY my_table (x2, x5, x7, x10) FROM PROGRAM 'mlr --csv lf cut -f x2,x5,x7,x10 /path/to/file.csv' WITH (FORMAT CSV, HEADER)
刚到这里是为了寻求一种只加载列子集的解决方案,但显然这是不可能的。因此,使用 awk (或cut
)将想要的列提取到新文件中new_file
:
$ awk '{print $2, $5, $7, $10}' file > new_file
并加载new_file
. 您可以将输出直接通过管道传输到psql
:
$ cut -d \ -f 2,5,7,10 file |
psql -h host -U user -c "COPY table(col1,col2,col3,col4) FROM STDIN DELIMITER ' '" database
注意COPY
,不是\COPY
。
更新:
正如评论中指出的那样,上述示例都不能处理数据中的引号分隔符。换行符也是如此,因为 awk 或cut
不知道 CSV。不过,可以使用 GNU awk 处理带引号的分隔符。
这是一个三列文件:
$ cat file
1,"2,3",4
使用 GNU awk 的FPAT
变量,即使引用的字段中有字段分隔符,我们也可以更改字段的顺序(或获取它们的子集):
$ gawk 'BEGIN{FPAT="([^,]*)|(\"[^\"]+\")";OFS=","}{print $2,$1,$3}' file
"2,3",1,4
解释:
$ gawk '
BEGIN { # instead of field separator FS
FPAT="([^,]*)|(\"[^\"]+\")" # ... we define field pattern FPAT
OFS="," # output field separator OFS
}
{
print $2,$1,$3 # change field order
# print $2 # or get a subset of fields
}' file
请注意,这FPAT
只是 GNU awk。对于其他 awks,它只是一个常规变量。
您可以进一步采纳 James Brown 的建议,并在一行中执行所有操作:
$ awk -F ',' '{print $2","$5","$7","$10}' file | psql -d db -c "\copy MyTable from STDIN csv header"
如果导入的行数对您来说并不重要,您还可以:
创建两个表:
然后创建:
一个触发函数,您可以在其中将所需的列插入 t2 并返回 NULL 以防止该行被插入 t1
调用此函数的 t1 (BEFORE INSERT FOR EACH ROW) 的触发器。
特别是对于较大的 csv 文件,BEFORE INSERT 触发器对于预先过滤具有某些属性的行也很有用,您也可以进行类型转换。
要将电子表格(Excel 或 OpenOffice Calc)中的数据加载到 postgreSQL 中:
将电子表格页面保存为 CSV 文件。首选方法是在 OpenOffice Calc 上打开电子表格并进行保存。在“导出到文本文件”窗口中,选择字符集为 Unicode (UTF8)、字段分隔符:“,”和文本分隔符“”。将显示消息说只保存活动工作表。注意:此文件必须保存在文件夹中,但不能保存在桌面上,并且必须以 UTF8 格式保存(默认的 postgreSQL 是 UTF8 编码的升级)。如果保存在桌面上,postgreSQL 将给出“拒绝访问”消息并且不会上传。
在 PostgreSQL 中,创建一个与电子表格具有相同列数的空表。
注意:在每一列上,列名必须相同,数据类型必须相同。另外,请记住字符随足够字段而变化的数据长度。
然后在 postgreSQL 上,在 SQL 窗口上,输入代码:
复制 "ABC"."def" 从 E'C:\\tmp\\blabla.csv' delimiters ',' CSV HEADER;
注意:这里 C:\\tmp 是保存 CSV 文件“blabla”的文件夹。“ABC”.“def”是在 postgreSQL 上创建的表,其中“ABC”是模式,“def”是实际表。然后按顶部的绿色按钮执行“执行查询”。当 CSV 表在每列的开头都有标题时,需要“CSV HEADER”。
如果一切正常,则不会显示错误消息,并且 CSV 文件中的表数据将被加载到 postgreSQL 表中。但如果出现错误信息,请执行以下操作:
如果错误消息表明特定列的数据太长,则增加列大小。这主要发生在字符和字符变化列上。然后再次运行“执行查询”命令。
如果错误消息表明数据类型与特定列不匹配,则更改 postgreSQL 表列上的数据类型以匹配 CSV 表中的数据类型。
在您的情况下,创建 CSV 文件后,删除不需要的列并匹配 postgre 表中的列。
一种快速的方法是将表复制到本地目录是:
\copy (select * from table_name) to 'data.csv' CSV;