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拒绝表功能。默认情况下,此功能会创建两个临时表。
reject_scans
:存储关于CSV扫描器参数的信息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_scans
和 reject_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” |