从标准输入复制 CSV 文件会引发“列缺失数据”

Posted

技术标签:

【中文标题】从标准输入复制 CSV 文件会引发“列缺失数据”【英文标题】:Copying a CSV file from stdin throws "missing data for column" 【发布时间】:2019-10-14 21:51:30 【问题描述】:

我有一些从 postgres 导出的数据,使用电子表格进行了一些修改,我知道希望将数据返回到表中,但我一直在导入失败:

cat extract.csv  | psql -h 10.135.0.44 myapp myapp -f copy-user.sql`
psql:copy-user.sql:7: ERROR:  missing data for column "email"
CONTEXT:  COPY to_update, line 1: ""

下面提供了实际数据。我首先将 CSV 文件从 DOS 转换为 Unix 样式的行尾。这似乎并不重要。

复制用户.sql

COPY "to_update" 
FROM STDIN
WITH DELIMITER ';' CSV;

extract.csv

bfb92e29-1d2c-45c4-b9ab-357a3ac7ad13;test@test90239023783457843.com;x
aeccc3ea-cc1f-43ef-99ff-e389d5d63b22;tester@testerkjnaefgjnwerg.no;x
9cec13ae-c880-4371-9b1c-dd201f5cf233;bloblo@gmail.com;x
aeada2bc-a362-4f3e-80f2-06a717206802;vet@gmail.com;x
fb85ddd8-7d17-4d41-8bc3-213b1e469506;navnnavnesen@ptflow.com;x
528e1f2e-1baa-483b-bc8c-85f993014696;kklk@hotmail.com;x
dbc8a9c1-56cf-4589-8b2c-cf1a2e0832ed;ghiiii@hotmail.com;x
fbf23553-baa2-410a-8f96-32b5c4deb0c7;lala@lala.no;x
e22ec0de-06f9-428a-aa3e-171c38f9a1f7;x2@gmail.com;x
8e8d0f73-8eb7-43b4-8019-b79042731b97;mail@mail.com;x

to_update的表定义

create table to_update(id text, email text, text char);

-- also tried this variant, but same error
-- create table to_update(id uuid, email text, text char);

编辑:附加信息

在我的本地机器上似乎没有发生完全相同的事情:

$ cat extract.csv  | psql postgres -f copy-user.sql
Timing is on.
Line style is unicode.
Border style is 2.
Null display is "[NULL]".
Expanded display is used automatically.
COPY 0
Time: 0.430 ms

它仍然不起作用(因为它只是复制 0 行),但至少它不会引发错误。这表明它与环境(版本、区域设置等)有关。

本地机器(不会抛出错误)

$ psql --version
psql (PostgreSQL) 10.6

$ psql postgres -c "SHOW server_version;"
Timing is on.
Line style is unicode.
Border style is 2.
Null display is "[NULL]".
Expanded display is used automatically.
┌────────────────┐
│ server_version │
├────────────────┤
│ 10.6           │
└────────────────┘
(1 row)

Time: 40.960 ms

$ printenv | grep LC
LC_CTYPE=UTF-8

远程服务器(引发错误)

$ psql --version  # this is the client, not the same physical server as the db
psql (PostgreSQL) 9.5.12

$ psql -h 10.135.0.44 myapp myapp -c "SHOW server_version;"
Password for user pete: 
 server_version 
----------------
 9.5.12
(1 row)

$ printenv | grep LC
LC_ALL=C.UTF-8
LC_CTYPE=UTF-8
LANG=C.UTF-8

extract.csv 的十六进制转储(全部 7 行)

$ wc -l extract.csv 
10 extract.csv

$ hexdump -C extract.csv 
00000000  62 66 62 39 32 65 32 39  2d 31 64 32 63 2d 34 35  |bfb92e29-1d2c-45|
00000010  63 34 2d 62 39 61 62 2d  33 35 37 61 33 61 63 37  |c4-b9ab-357a3ac7|
00000020  61 64 31 33 3b 74 65 73  74 40 74 65 73 74 39 30  |ad13;test@test90|
00000030  32 33 39 30 32 33 37 38  33 34 35 37 38 34 33 2e  |239023783457843.|
00000040  63 6f 6d 3b 78 0a 61 65  63 63 63 33 65 61 2d 63  |com;x.aeccc3ea-c|
00000050  63 31 66 2d 34 33 65 66  2d 39 39 66 66 2d 65 33  |c1f-43ef-99ff-e3|
00000060  38 39 64 35 64 36 33 62  32 32 3b 74 65 73 74 65  |89d5d63b22;teste|
00000070  72 40 74 65 73 74 65 72  6b 6a 6e 61 65 66 67 6a  |r@testerkjnaefgj|
00000080  6e 77 65 72 67 2e 6e 6f  3b 78 0a 39 63 65 63 31  |nwerg.no;x.9cec1|
00000090  33 61 65 2d 63 38 38 30  2d 34 33 37 31 2d 39 62  |3ae-c880-4371-9b|
000000a0  31 63 2d 64 64 32 30 31  66 35 63 66 32 33 33 3b  |1c-dd201f5cf233;|
000000b0  62 6c 6f 62 6c 6f 40 67  6d 61 69 6c 2e 63 6f 6d  |bloblo@gmail.com|
000000c0  3b 78 0a 61 65 61 64 61  32 62 63 2d 61 33 36 32  |;x.aeada2bc-a362|
000000d0  2d 34 66 33 65 2d 38 30  66 32 2d 30 36 61 37 31  |-4f3e-80f2-06a71|
000000e0  37 32 30 36 38 30 32 3b  76 65 74 40 67 6d 61 69  |7206802;vet@gmai|
000000f0  6c 2e 63 6f 6d 3b 78 0a  66 62 38 35 64 64 64 38  |l.com;x.fb85ddd8|
00000100  2d 37 64 31 37 2d 34 64  34 31 2d 38 62 63 33 2d  |-7d17-4d41-8bc3-|
00000110  32 31 33 62 31 65 34 36  39 35 30 36 3b 6e 61 76  |213b1e469506;nav|
00000120  6e 6e 61 76 6e 65 73 65  6e 40 70 74 66 6c 6f 77  |nnavnesen@ptflow|
00000130  2e 63 6f 6d 3b 78 0a 35  32 38 65 31 66 32 65 2d  |.com;x.528e1f2e-|
00000140  31 62 61 61 2d 34 38 33  62 2d 62 63 38 63 2d 38  |1baa-483b-bc8c-8|
00000150  35 66 39 39 33 30 31 34  36 39 36 3b 6b 6b 6c 6b  |5f993014696;kklk|
00000160  40 68 6f 74 6d 61 69 6c  2e 63 6f 6d 3b 78 0a 64  |@hotmail.com;x.d|
00000170  62 63 38 61 39 63 31 2d  35 36 63 66 2d 34 35 38  |bc8a9c1-56cf-458|
00000180  39 2d 38 62 32 63 2d 63  66 31 61 32 65 30 38 33  |9-8b2c-cf1a2e083|
00000190  32 65 64 3b 67 68 69 69  69 69 40 68 6f 74 6d 61  |2ed;ghiiii@hotma|
000001a0  69 6c 2e 63 6f 6d 3b 78  0a 66 62 66 32 33 35 35  |il.com;x.fbf2355|
000001b0  33 2d 62 61 61 32 2d 34  31 30 61 2d 38 66 39 36  |3-baa2-410a-8f96|
000001c0  2d 33 32 62 35 63 34 64  65 62 30 63 37 3b 6c 61  |-32b5c4deb0c7;la|
000001d0  6c 61 40 6c 61 6c 61 2e  6e 6f 3b 78 0a 65 32 32  |la@lala.no;x.e22|
000001e0  65 63 30 64 65 2d 30 36  66 39 2d 34 32 38 61 2d  |ec0de-06f9-428a-|
000001f0  61 61 33 65 2d 31 37 31  63 33 38 66 39 61 31 66  |aa3e-171c38f9a1f|
00000200  37 3b 78 32 40 67 6d 61  69 6c 2e 63 6f 6d 3b 78  |7;x2@gmail.com;x|
00000210  0a 38 65 38 64 30 66 37  33 2d 38 65 62 37 2d 34  |.8e8d0f73-8eb7-4|
00000220  33 62 34 2d 38 30 31 39  2d 62 37 39 30 34 32 37  |3b4-8019-b790427|
00000230  33 31 62 39 37 3b 6d 61  69 6c 40 6d 61 69 6c 2e  |31b97;mail@mail.|
00000240  63 6f 6d 3b 78 0a                                 |com;x.|
00000246

【问题讨论】:

错误信息似乎表明第一行是空的。你能显示head extract.csv | cat -n的输出吗? 你可以看到整个文件。全部十行。我还添加了一个 hexdump 来表明没有魔法 :-) 我怀疑 Postgres 9.5.12 有一些错误,以后的版本没有。 原来 Postgres 9.5.12 没有处理拆分成多行的复制语句。 Postgres 10 没有这个错误。 【参考方案1】:

我认为您希望 \copy ... from pstdin... 在一行中。开头的反斜杠和 pstdin 而不是 stdin 都是故意的。

这个邮件列表线程:psql -f COPY from STDIN 解释了问题和解决方案。

COPY FROM STDIN 期望在 COPY 命令之后内联数据,就像在转储文件中一样,而不是来自 psql 进程的标准输入。

邮件列表中的相关 sn-p 总结替代方案

我希望将 COPY 命令存储在单独的文件中 指定输入文件名。我想向它提供数据 调用 psql 的 shell 脚本

"STDIN:所有行都是从发出 命令” - 据我所知,这适用于 COPY 和 \COPY。换句话说,输入文件必须包含命令和数据。

我找到了一些解决方案来实现我的目标:

1) 使用 COPY FROM STDIN cat event.csv | psql -c "$(cat event.sql)"

2) 使用 COPY FROM STDIN psql -f

3) 使用 \COPY FROM PSTDIN cat event.csv | psql -f event.sql

4) 使用 \COPY FROM STDIN psql -f

我不喜欢 \COPY 是它必须在一行上。确实 它不能分成多行

【讨论】:

【参考方案2】:

在我的设置中进行以下工作:

cat extract.csv | psql -d db_name -U user_name -c "copy to_update from stdin with delimiter ';' csv"

psql -d db_name -U user_name -c "\copy public.to_update(id, email, text) from '/path_to/extract.csv' with delimiter ';' csv"

【讨论】:

这不是一回事吗 - 为什么它对你的情况有效? @oligofren 我也这么认为,但有点困惑。我能够通过在 sql 文件中添加新行来重现您的错误。 查看 Daniel 的回答以获得非常有趣的概述。【参考方案3】:

关于实际抛出的错误,经过一些调试,我发现这个错误只发生在 Postgres 9.5.12 上,而不是我运行 10.6 的本地数据库。那是在 sql 文件中使用完全相同的脚本。

Postgres 9.5.12 不处理多行 COPY FROM STDIN 语句!删除换行符以使整个表达式在一行上使其运行。但是,它仍然没有工作,因为它仍然显示 0 行被复制,但这确实是一个不同的问题......虽然克里希纳正在做一些事情......我会发布一个单独的问题为此并将其链接起来。

【讨论】:

以上是关于从标准输入复制 CSV 文件会引发“列缺失数据”的主要内容,如果未能解决你的问题,请参考以下文章

Eclipse:CSV 解析标准输入时未检测到 EOF

Python:我无法从函数中的标准输入解析 csv

连接大型 CSV 文件中单词的最有效方法:pandas 还是 Python 标准库? [复制]

将 csv 文件从 s3 复制到 redshift 的问题

当我尝试从文本文件输入值时,为啥我的类列表(在 C++ 中)会引发错误?

如何根据列名将数据从 CSV 复制到目标表?