如何使用 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 元命令忽略错误的主要内容,如果未能解决你的问题,请参考以下文章

psql备份csv文件

错误:在 psql 中使用 \copy 时缺少列数据

Docker - 如何在postgres容器中运行psql命令?

PSQL /copy : 变量替换不起作用 | PostgreSQL 11

PostgreSQL 基础知识:psql 提示和技巧

如何解决这个问题以使用 psql? | psql:错误:致命:角色“postgres”不存在