如何使用 psql \copy 元命令忽略错误
Posted
技术标签:
【中文标题】如何使用 psql \\copy 元命令忽略错误【英文标题】:How to ignore errors with psql \copy meta-command如何使用 psql \copy 元命令忽略错误 【发布时间】:2016-08-06 16:07:24 【问题描述】:我正在将psql
与 PostgreSQL 数据库和以下 copy
命令一起使用:
\COPY isa (np1, np2, sentence) FROM 'c:\Downloads\isa.txt' WITH DELIMITER '|'
我明白了:
ERROR: extra data after last expected column
如何跳过有错误的行?
【问题讨论】:
【参考方案1】:如果不跳过整个命令,直到 Postgres 14(包括 Postgres 14),您就无法跳过错误。目前没有更复杂的错误处理。
\copy
只是 SQL COPY
的包装器,它通过 psql 引导结果。 COPY
的手册:
COPY
在出现第一个错误时停止操作。 这在COPY TO
事件中不会导致问题,但目标表会 已经在COPY FROM
中收到了较早的行。这些行将 不可见或不可访问,但它们仍然占用磁盘空间。这 如果 失败发生在大型复制操作中。您可能希望 调用VACUUM
来回收浪费的空间。
我的大胆强调。并且:
COPY FROM
如果输入文件的任何一行包含 比预期更多或更少的列。
COPY
是一种非常快速的导入/导出数据的方式。复杂的检查和错误处理会减慢它的速度。
有一个attempt to add error logging to COPY
in Postgres 9.0,但从未提交过。
解决方案
改为修复您的输入文件。
如果您的输入文件中有一个或多个附加列并且该文件否则是一致的,您可以将虚拟列添加到您的表 isa
并在之后删除这些列。或者(使用生产表进行清理)导入临时临时表并将 INSERT
选定的列(或表达式)从那里导入目标表 isa
。
带有详细说明的相关答案:
How to update selected rows with values from a CSV file in Postgres? COPY command: copy only specific columns from csv【讨论】:
如果我添加额外的表格列,我可以使用类似于我的原始命令(带有额外的列)的东西,还是需要可选列的额外选项? 我得到:错误:“dummy1”列缺少数据 @Superdooperhero:就COPY
而言,这些列就像其他列一样。您的“丢失数据”错误表明您的输入文件不一致 - 或者您没有使用正确的分隔符或转义字符。不管怎样,我宁愿选择我提到的第二个选项:临时登台表。但是你也需要一个一致的文件。
对于多年后偶然发现此答案的任何人:如果您的输入数据中只有几行格式错误,您可以运行\copy
命令,postgres 将报告错误所在的行号。然后,您可以使用 sed -i '5d' input.tsv
(其中 5 是行号)删除此行并再次尝试运行 \copy
。【参考方案2】:
25 年来 Postgres 没有-ignore-errors
标志或COPY
命令选项太糟糕了。在这个大数据时代,你会得到很多脏记录,修复每个异常值的项目成本可能非常高。
我不得不以这种方式解决问题:
-
复制原表并命名为
dummy_original_table
在原始表中,创建如下触发器:
CREATE OR REPLACE FUNCTION on_insert_in_original_table() RETURNS trigger AS $$
DECLARE
v_rec RECORD;
BEGIN
-- we use the trigger to prevent 'duplicate index' error by returning NULL on duplicates
SELECT * FROM original_table WHERE primary_key=NEW.primary_key INTO v_rec;
IF v_rec IS NOT NULL THEN
RETURN NULL;
END IF;
BEGIN
INSERT INTO original_table(datum,primary_key) VALUES(NEW.datum,NEW.primary_key)
ON CONFLICT DO NOTHING;
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
RETURN NULL;
END;
-
将副本运行到虚拟表中。那里不会插入记录,但会全部插入到 original_table 中
psql dbname -c \copy dummy_original_table(datum,primary_key) FROM '/home/user/data.csv' delimiter E'\t'
【讨论】:
【参考方案3】:这是一种解决方案——一次导入批处理文件。性能可能会慢很多,但对于您的场景可能就足够了:
#!/bin/bash
input_file=./my_input.csv
tmp_file=/tmp/one-line.csv
cat $input_file | while read input_line; do
echo "$input_line" > $tmp_file
psql my_database \
-c "\
COPY my_table \
FROM `$tmp_file` \
DELIMITER '|'\
CSV;\
"
done
此外,您可以修改脚本以捕获psql
stdout/stderr 并退出
状态,如果退出状态不为零,则将 $input_line
和捕获的 stdout/stderr 回显到 stdin 和/或将其附加到文件中。
【讨论】:
【参考方案4】:解决方法:使用sed
删除报告的错误行并再次运行\copy
更高版本的 Postgres(包括 Postgres 13),将报告错误的行号。然后,您可以使用 sed
删除该行并再次运行 \copy,例如,
#!/bin/bash
bad_line_number=5 # assuming line 5 is the bad line
sed $bad_line_numberd < input.csv > filtered.csv
[根据the comment from @Botond_Balázs]
【讨论】:
以上是关于如何使用 psql \copy 元命令忽略错误的主要内容,如果未能解决你的问题,请参考以下文章
Docker - 如何在postgres容器中运行psql命令?