如何使用psql \ copy meta-command忽略错误

时间:2016-04-14 21:30:16

标签: postgresql error-handling psql postgresql-copy

我正在使用psql和PostgreSQL数据库以及以下copy命令:

\COPY isa (np1, np2, sentence) FROM 'c:\Downloads\isa.txt' WITH DELIMITER '|'

我明白了:

ERROR:  extra data after last expected column

如何跳过有错误的行?

4 个答案:

答案 0 :(得分:12)

如果不跳过包括Postgres 9.5在内的整个命令,则不能跳过错误。目前没有更复杂的错误处理。

\copy只是SQL COPY的包装器,它通过psql传递结果。 COPY的手册:

  

COPY在第一次错误时停止操作。这不应该导致   在COPY TO的情况下出现问题,但目标表已经存在   已收到COPY FROM中的早期行。这些行不会   可见或可访问,但它们仍然占用磁盘空间。这有可能   如果失败,则相当于浪费了相当多的磁盘空间   发生在大型复制操作中。你可能希望调用   VACUUM恢复浪费的空间。

大胆强调我的。和

  如果输入文件的任何行包含,

COPY FROM将引发错误   列数多于或少于预期。

有一个attempt to add error logging to COPY in Postgres 9.0,由Aster Data带头,但它从未被提交过。该公司后来被Teradata收购,所以我怀疑他们仍然在追求这个项目。

解决方案

修改输入文件。

如果输入文件中有一个或多个附加列,并且文件另外一致,则可以向表isa添加虚拟列,然后删除它们。或者(带有生产表的清洁工)从那里导入临时临时表和INSERT个选定列(或表达式)到目标表isa

相关答案及详细说明:

答案 1 :(得分:4)

太糟糕了,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'

答案 2 :(得分:1)

这是一种解决方案——一次一行导入批处理文件。性能可能会慢得多,但对于您的场景来说可能已经足够了:

#!/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 和/或将其附加到文件中。

答案 3 :(得分:0)

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

[每 the comment from @Botond_Balázs ]