You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

PostgreSQL中COPY FROM STDIN失效但指定路径正常,求故障原因

问题解析:PostgreSQL \copy通过cat管道STDIN导入失败,但指定文件路径成功

先还原你的场景:你创建了stock_data表,想用临时表stock_data_tmp中转导入多个CSV,计划用cat合并文件后管道传给SQL脚本,但执行cat /path/to/20140102.txt | psql -d my_db_name -f ~/path/to/script/update_stock_data.sql时报错ERROR: missing data for column "date",但直接在脚本里写\copy stock_data_tmp FROM '/path/to/20140102.txt' WITH csv;就成功。

为什么会出现这个问题?

这里有两个关键原因:

  1. psql -f与STDIN管道的冲突
    当你使用psql -f script.sql时,psql会把指定的脚本文件作为它的主要输入源,这时候你通过管道传入的CSV数据会被psql忽略——因为psql已经在专注读取脚本文件的内容了,不会再去读取管道过来的STDIN。所以当脚本执行到\copy stock_data_tmp FROM STDIN WITH CSV;时,实际上没有读到任何CSV数据,自然会报“缺少列数据”的错误。

  2. 脚本里的小笔误(容易被忽略)
    你的临时表列名是ddate,但在INSERT语句里写的是to_date(date, 'YYYYMMDD')——这里引用了不存在的date列。不过你直接指定文件路径时没报错?可能是你测试时刚好修正了这个点,或者PostgreSQL的错误提示被掩盖了,但这个问题必须修正,否则即使管道问题解决了,后续插入也会失败。

解决方案

针对管道导入的需求,有两种常用的解决方式:

方案1:用psql -c替代-f,将脚本嵌入命令行

把SQL脚本内容直接作为psql -c的参数,这样psql会先执行SQL命令,然后\copy会正确读取管道传入的STDIN数据:

cat /path/to/20140102.txt | psql -d my_db_name -c "
CREATE TEMPORARY TABLE IF NOT EXISTS stock_data_tmp (
 code varchar,
 ddate varchar,
 open decimal,
 high decimal,
 low decimal,
 close decimal,
 volume decimal,
 UNIQUE (code, ddate)
 );
\copy stock_data_tmp FROM STDIN WITH CSV;
INSERT INTO stock_data SELECT code, to_date(ddate, 'YYYYMMDD'), open, high, low, close, volume FROM stock_data_tmp;
DROP TABLE stock_data_tmp;
"

注意这里已经把to_date(date, ...)修正为to_date(ddate, ...)了。

方案2:合并脚本与CSV内容后传入psql

如果你还是想保留独立的脚本文件,可以把脚本和CSV文件的内容合并后一起传给psql(不用-f参数):

cat ~/path/to/script/update_stock_data.sql /path/to/20140102.txt | psql -d my_db_name

但要确保你的脚本文件最后一行就是\copy stock_data_tmp FROM STDIN WITH CSV;,这样psql执行完脚本的前半部分后,会自动读取后续的CSV内容作为\copy的输入。如果要导入多个CSV文件,直接把所有文件都追加到cat后面即可:

cat ~/path/to/script/update_stock_data.sql /path/to/20140102.txt /path/to/20140103.txt | psql -d my_db_name

同样,记得先修正脚本里的dateddate的笔误。

内容的提问来源于stack exchange,提问作者pragMATHiC

火山引擎 最新活动