处理杂乱的数据

使用 COPY 加载数据有两个主要阶段:解析和加载。在解析过程中,如果 COPY 遇到错误,它会拒绝错误数据并继续加载数据。每当 COPY 无法解析某行数据时,便会创建拒绝数据。下面是一些可能导致拒绝行的解析器错误:

  • 解析器选项不受支持

  • 数据要加载到的表的数据类型不正确,包括集合成员的数据类型不正确

  • 所用解析器的上下文格式错误

  • 缺少分隔符

COPY 可以选择在加载阶段转换数据时拒绝数据并继续加载。此行为由配置参数控制。默认情况下,如果在加载阶段遇到错误,COPY 会中止加载。

一些可选参数可让您确定 COPY 处理拒绝的严格程度。例如,可让 COPY 在拒绝单行时就失败,也可以在加载失败前允许特定数量的拒绝。此部分介绍用于确定 COPY 如何处理被拒绝数据的参数。

保存拒绝行(REJECTED DATA 和 EXCEPTIONS)

COPY 语句自动将每个拒绝行的副本保存在 rejections 文件中。COPY 还将有关拒绝原因的相应解释保存在 exceptions 文件中。默认情况下,Vertica 将这两个文件保存在名为 CopyErrorLogs 的数据库编录子目录中,如本例中所示:

v_mart_node003_catalog\CopyErrorLogs\trans-STDIN-copy-from-rejected-data.1
v_mart_node003_catalog\CopyErrorLogs\trans-STDIN-copy-from-exceptions.1

您可以选择以其他两种方式之一保存 COPY 拒绝和异常:

  • 使用 REJECTED DATAEXCEPTIONS 选项将两个输出保存到您选择的位置。REJECTED DATA 记录拒绝的行,而 EXCEPTIONS 记录每行拒绝原因的描述。如果路径值是现有目录或以 '/' 结尾,或者加载包含多个源,则将文件写入该目录中。(如果该目录不存在,COPY 会创建该目录。)如果路径值是一个文件且写入多个文件,则 COPY 使用它作为文件前缀。

  • 使用 REJECTED DATA AS TABLE 选项。此选项将拒绝数据和异常描述写入同一个表。有关详细信息,请参阅将拒绝的数据保存到表中

如果将拒绝数据保存到表中,表文件则存储在数据子目录中。例如,在 Vmart 数据库安装中,拒绝数据表记录存储在 RejectionTableData 目录中,如下所示:

=> cd v_mart_node003_data\RejectionTableData\
=> ls
TABLE_REJECTED_RECORDS_"bg"_mytest01.example.-25441:0x6361_45035996273805099_1.1
TABLE_REJECTED_RECORDS_"bg"_mytest01.example.-25441:0x6361_45035996273805113_2.2
.
.
.
TABLE_REJECTED_RECORDS_"delimr"_mytest01.example.-5958:0x3d47_45035996273815749_1.1
TABLE_REJECTED_RECORDS_"delimr"_mytest01.example.-5958:0x3d47_45035996273815749_1.2

COPY LOCAL 拒绝数据

对于 COPY LOCAL 操作,如果您将 REJECTED DATAEXCEPTIONS 用于文件路径,则文件写入客户端。如果您希望拒绝在所有节点上可用,请使用 REJECTED DATA AS TABLE 而不是 REJECTED DATA

强制截断或拒绝行 (ENFORCELENGTH)

解析 CHAR、VARCHAR、BINARY 或 VARBINARY 数据时,行可能会超过目标表长度。默认情况下,COPY 会截断此类行,但不拒绝它们。使用 ENFORCELENGTH 选项拒绝超出目标表的行。

例如,如果将 'abc' 加载到指定为 VARCHAR(2) 的表列,COPY 则会将值截断成 'ab',然后再进行加载。使用 ENFORCELENGTH 选项加载同一行会导致 COPY 拒绝该行。

指定最大拒绝数 (REJECTMAX)

REJECTMAX 选项指定在加载失败前可以拒绝的最大逻辑记录数。拒绝行由批量加载过程中无法解析(或无法转换)成相应数据类型的数据组成。拒绝数据并不指示引用约束。有关使用约束以及在批量加载过程中强制执行约束的选项的信息,请参阅约束

当拒绝记录数等于 REJECTMAX 值时,加载将失败。如果未指定 REJECTMAX 值或该值为 0,则 COPY 允许的异常数无限制。

如果允许 COPY 在遇到转换错误时拒绝行然后继续,请考虑使用 REJECTMAX 限制影响。请参阅处理转换错误

处理转换错误

默认情况下,如果在执行转换时遇到错误,则 COPY 中止加载。这是默认设置,因为拒绝转换错误可能比拒绝解析错误成本更高。但是,有时您更愿意无论如何都加载数据并拒绝有问题的行,就像对待解析错误一样。

要让 COPY 将转换表达式中的错误视为解析错误,请将 CopyFaultTolerantExpressions 配置参数设置为 1。(请参阅常规参数。)在数据加载的表达式评估阶段,在转换期间被拒绝的行将被写入与解析期间被拒绝的行相同的目的地。使用 REJECTED DATAREJECTED DATA AS TABLE 指定输出位置。

如果数据包含一些错误行,您可能希望启用转换拒绝。通过启用这些拒绝,您可以加载大部分数据并继续。Vertica 建议在启用转换拒绝时使用 REJECTMAX

如果数据包含许多错误值,则加载非错误行的性能可能比解析器错误更差。

出现任何错误时中止数据加载 (ABORT ON ERROR)

使用 ABORT ON ERROR 选项是限制性最强的数据加载方式,因为不允许任何异常或拒绝。如果任何行遭拒,COPY 操作将停止。不会加载任何数据,而且 Vertica 回退命令。

如果您使用 ABORT ON ERROR 作为 CREATE EXTERNAL TABLE 语句的一部分,则每当查询引用外部表时都会使用该选项。违规错误保存在 COPY 异常或拒绝数据文件中。

了解行拒绝和回退错误

根据 COPY 遇到的错误类型,Vertica 将执行下列操作之一:

  • 拒绝违规行并将其他行加载到表中

  • 回退整个 COPY 语句,但不加载任何数据

COPY 无法解析包含以下任一项的行:

  • 不兼容数据类型

  • 缺失字段

  • 缺少分隔符

如果遇到以下任一条件,COPY 会回退语句:

  • 服务器端错误,如缺少内存

  • 违反主键或外键约束

  • 将 NULL 数据加载到 NOT NULL 列中

  • 转换错误(默认情况)

此示例演示当 Vertica 无法将行解析成所请求的数据类型时所发生的情况。例如,在下列 COPY 语句中,"a::INT + b::INT" 是一个 SQL 表达式,其中 ab 为衍生值:

=> CREATE TABLE t (i INT);
=> COPY t (a FILLER VARCHAR, b FILLER VARCHAR, i AS a::INT + b::INT)
   FROM STDIN;
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>> cat|dog
>> \.

Vertica 无法将行解析成所请求的数据类型,因而拒绝行:

ERROR 2827:  Could not convert "cat" from column "*FILLER*".a to an int8

如果 a 解析为 'cat'b 解析为 'dog',则下一个表达式 'cat'::INT + 'dog'::INT 将返回表达式评估程序错误:

=> SELECT 'cat'::INT + 'dog'::INT;
ERROR 3681:  Invalid input syntax for integer: "cat"

下列 COPY 语句也会回退,因为 Vertica 无法将行解析成所请求的数据类型:

=> COPY t (a FILLER VARCHAR, i AS a::INT) FROM STDIN;

在下列 COPY 语句中,Vertica 仅拒绝违规行但不回退语句。COPY 将 'cat' 直接解析为 INTEGER,而不是将 'cat' 行计算为 VARCHAR 类型。

=> COPY t (a FILLER INT, i AS a) FROM STDIN;

在以下示例中,转换错误被拒绝而不是中止加载。

=> ALTER DATABASE DEFAULT SET CopyFaultTolerantExpressions = 1;
ALTER DATABASE

=> CREATE TABLE sales (price INTEGER);
CREATE TABLE

=> COPY sales (f FILLER VARCHAR, price AS f::INT)
   FROM STDIN REJECTED DATA AS TABLE sales_rej;

=> COPY sales FROM STDIN REJECTED DATA AS TABLE sales_rej;
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>>1
>>2a
>>3
>>\.

=> SELECT * FROM sales;
 price
-------
     1
     3
(2 rows)

=> SELECT rejected_data, rejected_reason FROM sales_rej;
 rejected_data |                rejected_reason
---------------+-----------------------------------------------
 Tuple (2a)    | ERROR 2827:  Could not convert "2a" from column "*FILLER*".f to an int8
(1 row)

另请参阅