繁体   English   中英

如何使用 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只是 SQL COPY的包装器,它通过 psql 传递结果。 COPY手册:

COPY在出现第一个错误时停止操作。 这应该不会在COPY TO的情况下导致问题,但是目标表已经在COPY FROM收到了较早的行。 这些行将不可见或不可访问,但它们仍会占用磁盘空间。 如果故障发生在大型复制操作中,这可能会浪费大量磁盘空间。 您可能希望调用VACUUM来恢复浪费的空间。

大胆强调我的。 并且:

如果输入文件的任何行包含的列比预期的多COPY FROM将引发错误。

COPY是一种极其快速的数据导入/导出方式。 复杂的检查和错误处理会减慢它的速度。

尝试在 Postgres 9.0 中向COPY添加错误日志记录,但从未提交。

解决方案

改为修复您的输入文件。

如果您的输入文件中有一个或多个附加列,并且该文件在其他方面一致的,您可以向表isa添加虚拟列,然后再删除这些列。 或者(使用生产表进行清理)导入到临时登台表并将选定的列(或表达式)从那里INSERT到目标表isa

带有详细说明的相关答案:

太糟糕了,25 年来 Postgres 没有-ignore-errors标志或COPY命令选项。 在这个大数据时代,你会得到很多脏记录,项目修复每个异常值的成本可能非常高。

我不得不以这种方式解决问题:

  1. 复制原表并命名为dummy_original_table
  2. 在原始表中,创建一个这样的触发器:
    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;
  1. 将副本运行到虚拟表中。 那里不会插入任何记录,但都会插入到 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 回显到 stdin 和/或将其附加到文件中。

解决方法:使用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

[根据@Botond_Balázs 的评论]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM