如何使用 psql \copy 元命令忽略错误
How to ignore errors with psql \copy meta-command
我正在使用 psql
和 PostgreSQL 数据库以及以下 copy
命令:
\COPY isa (np1, np2, sentence) FROM 'c:\Downloads\isa.txt' WITH DELIMITER '|'
我得到:
ERROR: extra data after last expected column
如何跳过有错误的行?
如果不跳过包括 Postgres 14 在内的整个命令,您将无法跳过错误。目前没有更复杂的错误处理。
\copy
is just a wrapper around SQL COPY
通过 psql 传递结果。 COPY
的手册:
COPY
stops operation at the first error. This should not lead to problems in the event of a COPY TO
, but the target table will
already have received earlier rows in a COPY FROM
. These rows will
not be visible or accessible, but they still occupy disk space. This
might amount to a considerable amount of wasted disk space if the
failure happened well into a large copy operation. You might wish to
invoke VACUUM
to recover the wasted space.
大胆强调我的。并且:
COPY FROM
will raise an error if any line of the input file contains
more or fewer columns than are expected.
COPY
是一种极其快速的导入/导出数据的方法。复杂的检查和错误处理会减慢速度。
有一个 attempt to add error logging to COPY
in Postgres 9.0 但从未提交。
解决方案
改为修复您的输入文件。
如果您的输入文件中有一个或多个额外的列并且该文件在其他方面是一致的,您可以将虚拟列添加到您的tableisa
然后放下那些。或者(生产 tables 更清洁)导入到临时暂存 table 和 INSERT
选定的列(或表达式)到目标 table isa
从那里。
相关答案及详细说明:
- How to update selected rows with values from a CSV file in Postgres?
- COPY command: copy only specific columns from csv
遗憾的是 25 年来 Postgres 没有 -ignore-errors
标志或 COPY
命令的选项。在这个大数据时代,你会得到很多脏记录,修复每个离群值对项目来说成本非常高。
我不得不以这种方式解决问题:
- 复制原来的table并命名为
dummy_original_table
- 在原来的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;
- 运行复制一份到虚拟table。那里不会插入任何记录,但所有记录都将插入 original_table
psql dbname -c \copy dummy_original_table(datum,primary_key) FROM '/home/user/data.csv' delimiter E'\t'
这是一个解决方案 -- 一次导入一行批处理文件。性能可能会慢得多,但对于您的场景来说可能已经足够了:
#!/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 到标准输入 and/or 将其附加到文件中。
解决方法:再次使用 sed
和 运行 \copy
删除报告的错误行
Postgres以后的版本(包括Postgres 13),会报错的行号。然后,您可以使用 sed
和 运行 \copy 再次删除该行,例如,
#!/bin/bash
bad_line_number=5 # assuming line 5 is the bad line
sed ${bad_line_number}d < input.csv > filtered.csv
[根据 ]
我正在使用 psql
和 PostgreSQL 数据库以及以下 copy
命令:
\COPY isa (np1, np2, sentence) FROM 'c:\Downloads\isa.txt' WITH DELIMITER '|'
我得到:
ERROR: extra data after last expected column
如何跳过有错误的行?
如果不跳过包括 Postgres 14 在内的整个命令,您将无法跳过错误。目前没有更复杂的错误处理。
\copy
is just a wrapper around SQL COPY
通过 psql 传递结果。 COPY
的手册:
COPY
stops operation at the first error. This should not lead to problems in the event of aCOPY TO
, but the target table will already have received earlier rows in aCOPY FROM
. These rows will not be visible or accessible, but they still occupy disk space. This might amount to a considerable amount of wasted disk space if the failure happened well into a large copy operation. You might wish to invokeVACUUM
to recover the wasted space.
大胆强调我的。并且:
COPY FROM
will raise an error if any line of the input file contains more or fewer columns than are expected.
COPY
是一种极其快速的导入/导出数据的方法。复杂的检查和错误处理会减慢速度。
有一个 attempt to add error logging to COPY
in Postgres 9.0 但从未提交。
解决方案
改为修复您的输入文件。
如果您的输入文件中有一个或多个额外的列并且该文件在其他方面是一致的,您可以将虚拟列添加到您的tableisa
然后放下那些。或者(生产 tables 更清洁)导入到临时暂存 table 和 INSERT
选定的列(或表达式)到目标 table isa
从那里。
相关答案及详细说明:
- How to update selected rows with values from a CSV file in Postgres?
- COPY command: copy only specific columns from csv
遗憾的是 25 年来 Postgres 没有 -ignore-errors
标志或 COPY
命令的选项。在这个大数据时代,你会得到很多脏记录,修复每个离群值对项目来说成本非常高。
我不得不以这种方式解决问题:
- 复制原来的table并命名为
dummy_original_table
- 在原来的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;
- 运行复制一份到虚拟table。那里不会插入任何记录,但所有记录都将插入 original_table
psql dbname -c \copy dummy_original_table(datum,primary_key) FROM '/home/user/data.csv' delimiter E'\t'
这是一个解决方案 -- 一次导入一行批处理文件。性能可能会慢得多,但对于您的场景来说可能已经足够了:
#!/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 到标准输入 and/or 将其附加到文件中。
解决方法:再次使用 sed
和 运行 \copy
删除报告的错误行
Postgres以后的版本(包括Postgres 13),会报错的行号。然后,您可以使用 sed
和 运行 \copy 再次删除该行,例如,
#!/bin/bash
bad_line_number=5 # assuming line 5 is the bad line
sed ${bad_line_number}d < input.csv > filtered.csv
[根据