⌘+k ctrl+k
1.3 (稳定版)
搜索快捷键 cmd + k | ctrl + k
读取有问题的 CSV 文件

CSV文件形式多样,有些文件包含大量错误,使得干净地读取它们本身就很困难。为了帮助用户读取这些文件,DuckDB支持详细的错误消息、跳过错误行的功能,以及将错误行存储在临时表中以帮助用户进行数据清洗的步骤。

结构性错误

DuckDB支持检测和跳过多种不同的结构性错误。在本节中,我们将通过示例逐一介绍每种错误。为了方便示例说明,请考虑以下表格:

CREATE TABLE people (name VARCHAR, birth_date DATE);

DuckDB检测到以下错误类型:

  • CAST:当CSV文件中的列无法转换为预期的模式值时,会发生转换(Casting)错误。例如,行 Pedro,The 90s 会导致错误,因为字符串 The 90s 无法转换为日期类型。
  • MISSING COLUMNS:当CSV文件中的某行包含的列少于预期时,会发生此错误。在我们的示例中,我们预期有两列;因此,只有一列值的行(例如 Pedro)将导致此错误。
  • TOO MANY COLUMNS:当CSV文件中的某行包含的列多于预期时,会发生此错误。在我们的示例中,任何包含多于两列的行(例如 Pedro,01-01-1992,pdet)都将导致此错误。
  • UNQUOTED VALUE:CSV行中的引用值在末尾必须始终取消引用;如果引用值在整个过程中都保持引用状态,则会导致错误。例如,假设我们的扫描器使用 quote='"',则行 "pedro"holanda, 01-01-1992 将出现未引用值错误。
  • LINE SIZE OVER MAXIMUM:DuckDB有一个参数用于设置CSV文件的最大行大小,默认设置为2,097,152字节。假设我们的扫描器设置为 max_line_size = 25,则行 Pedro Holanda, 01-01-1992 将产生错误,因为它超过了25字节。
  • INVALID ENCODING:DuckDB支持UTF-8字符串、UTF-16和Latin-1编码。包含其他字符的行将产生错误。例如,行 pedro\xff\xff, 01-01-1992 将会引发问题。

CSV错误的解析

默认情况下,在执行CSV读取时,如果遇到任何结构性错误,扫描器将立即停止扫描过程并向用户抛出错误。这些错误旨在提供尽可能多的信息,以便用户可以直接在其CSV文件中评估它们。

这是一个完整错误消息的示例:

Conversion Error:
CSV Error on Line: 5648
Original Line: Pedro,The 90s
Error when converting column "birth_date". date field value out of range: "The 90s", expected format is (DD-MM-YYYY)

Column date is being converted as type DATE
This type was auto-detected from the CSV file.
Possible solutions:
* Override the type for this column manually by setting the type explicitly, e.g., types={'birth_date': 'VARCHAR'}
* Set the sample size to a larger value to enable the auto-detection to scan more values, e.g., sample_size=-1
* Use a COPY statement to automatically derive types from an existing table.

  file= people.csv
  delimiter = , (Auto-Detected)
  quote = " (Auto-Detected)
  escape = " (Auto-Detected)
  new_line = \r\n (Auto-Detected)
  header = true (Auto-Detected)
  skip_rows = 0 (Auto-Detected)
  date_format = (DD-MM-YYYY) (Auto-Detected)
  timestamp_format =  (Auto-Detected)
  null_padding=0
  sample_size=20480
  ignore_errors=false
  all_varchar=0

第一个信息块提供了错误发生的位置信息,包括行号、原始CSV行以及哪个字段有问题。

Conversion Error:
CSV Error on Line: 5648
Original Line: Pedro,The 90s
Error when converting column "birth_date". date field value out of range: "The 90s", expected format is (DD-MM-YYYY)

第二个信息块提供了潜在的解决方案。

Column date is being converted as type DATE
This type was auto-detected from the CSV file.
Possible solutions:
* Override the type for this column manually by setting the type explicitly, e.g., types={'birth_date': 'VARCHAR'}
* Set the sample size to a larger value to enable the auto-detection to scan more values, e.g., sample_size=-1
* Use a COPY statement to automatically derive types from an existing table.

由于此字段的类型是自动检测的,因此建议将该字段定义为 VARCHAR 类型,或者充分利用数据集进行类型检测。

最后,最后一个信息块显示了扫描器中可能导致错误的一些选项,并指明它们是自动检测的还是用户手动设置的。

使用 ignore_errors 选项

在某些情况下,CSV文件可能存在多个结构性错误,用户可能只希望跳过这些错误并读取正确的数据。通过使用 ignore_errors 选项,可以读取包含错误的CSV文件。启用此选项后,将忽略包含会导致CSV解析器生成错误的数据的行。在我们的示例中,我们将演示一个CAST错误,但请注意,结构性错误部分中描述的任何错误都会导致错误行被跳过。

例如,请考虑以下CSV文件:faulty.csv

Pedro,31
Oogie Boogie, three

如果您读取CSV文件时指定第一列为 VARCHAR,第二列为 INTEGER,则文件加载将失败,因为字符串 three 无法转换为 INTEGER

例如,以下查询将抛出一个转换错误。

FROM read_csv('faulty.csv', columns = {'name': 'VARCHAR', 'age': 'INTEGER'});

但是,如果设置了 ignore_errors,文件中的第二行将被跳过,只输出完整的首行。例如:

FROM read_csv(
    'faulty.csv',
    columns = {'name': 'VARCHAR', 'age': 'INTEGER'},
    ignore_errors = true
);

输出结果:

name age
Pedro 31

需要注意的是,CSV解析器受投影下推优化的影响。因此,如果我们只选择 `name` 列,那么两行都将被视为有效,因为年龄字段上的转换错误将永远不会发生。例如:

SELECT name
FROM read_csv('faulty.csv', columns = {'name': 'VARCHAR', 'age': 'INTEGER'});

输出结果:

name
Pedro
Oogie Boogie

检索有缺陷的CSV行

能够读取有缺陷的CSV文件固然重要,但对于许多数据清洗操作而言,还需要确切知道哪些行损坏了以及解析器发现了哪些错误。对于这类场景,可以使用DuckDB的CSV拒绝表功能。默认情况下,此功能会创建两个临时表。

  1. reject_scans:存储关于CSV扫描器参数的信息
  2. reject_errors:存储关于每个有缺陷的CSV行及其发生在哪一个CSV扫描器中的信息。

请注意,结构性错误部分中描述的任何错误都将存储在拒绝表中。此外,如果某行存在多个错误,则同一行将存储多个条目,每个错误一个条目。

拒绝扫描信息

CSV拒绝扫描表返回以下信息:

列名 描述 类型
scan_id DuckDB中用于表示该扫描器的内部ID UBIGINT
file_id 一个扫描器可能处理多个文件,因此 file_id 表示扫描器中的一个唯一文件 UBIGINT
file_path 文件路径 VARCHAR
delimiter 使用的分隔符,例如分号(;) VARCHAR
quote 使用的引用符,例如双引号(") VARCHAR
escape 使用的引用符,例如双引号(") VARCHAR
newline_delimiter 使用的换行符分隔符,例如 \r\n VARCHAR
skip_rows 如果文件顶部有任何行被跳过 UINTEGER
has_header 文件是否包含标题行 BOOLEAN
columns 文件的模式(即所有列名和类型) VARCHAR
date_format 日期类型使用的格式 VARCHAR
timestamp_format 时间戳类型使用的格式 VARCHAR
user_arguments 用户手动设置的任何额外扫描器参数 VARCHAR

拒绝错误信息

CSV拒绝错误表返回以下信息:

列名 描述 类型
scan_id DuckDB中用于表示该扫描器的内部ID,用于与拒绝扫描表(reject scans tables)进行关联 UBIGINT
file_id file_id 表示扫描器中的一个唯一文件,用于与拒绝扫描表(reject scans tables)进行关联 UBIGINT
line CSV文件中错误发生的行号。 UBIGINT
line_byte_position 错误发生行起点的字节位置。 UBIGINT
byte_position 错误发生的字节位置。 UBIGINT
column_idx 如果错误发生在特定列中,则为该列的索引。 UBIGINT
列名 (column_name) 如果错误发生在特定列中,则为该列的名称。 VARCHAR
error_type 发生的错误类型。 ENUM
csv_line 原始CSV行。 VARCHAR
error_message DuckDB产生的错误消息。 VARCHAR

参数

以下列出的参数用于 read_csv 函数,以配置CSV拒绝表。

名称 描述 类型 默认值
store_rejects 如果设置为 true,文件中的任何错误将被跳过并存储在默认的拒绝临时表中。 BOOLEAN False
rejects_scan 一个临时表的名称,其中存储了有缺陷CSV文件的扫描信息。 VARCHAR reject_scans
rejects_table 一个临时表的名称,其中存储了CSV文件中有缺陷行的信息。 VARCHAR reject_errors
rejects_limit 将记录在拒绝表中的CSV文件有缺陷记录数量的上限。当不应用任何限制时,使用0。 BIGINT 0

要将有缺陷的CSV行信息存储在拒绝表中,用户只需将 store_rejects 选项设置为 true。例如:

FROM read_csv(
    'faulty.csv',
    columns = {'name': 'VARCHAR', 'age': 'INTEGER'},
    store_rejects = true
);

然后您可以查询 reject_scansreject_errors 表,以检索有关被拒绝元组的信息。例如:

FROM reject_scans;

输出结果:

scan_id file_id file_path delimiter quote escape newline_delimiter skip_rows has_header columns date_format timestamp_format user_arguments
5 0 faulty.csv , " " \n 0 false {'name': 'VARCHAR','age': 'INTEGER'}     store_rejects=true
FROM reject_errors;

输出结果:

scan_id file_id line line_byte_position byte_position column_idx 列名 (column_name) error_type csv_line error_message
5 0 2 10 23 2 age CAST Oogie Boogie, three 转换列“age”时出错。无法将字符串“ three”转换为“INTEGER”