ClickHouse 支持从 CSV 导入数据,也支持将数据导出为 CSV。由于 CSV 文件的格式细节可能各不相同,包括表头、自定义分隔符和转义符等,ClickHouse 提供了相应的格式与设置,以便高效应对各种情况。
从 CSV 文件导入数据
在导入数据之前,先创建一个具有相应结构的表:
CREATE TABLE sometable
(
`path` String,
`month` Date,
`hits` UInt32
)
ENGINE = MergeTree
ORDER BY tuple(month, path)要将 CSV 文件 中的数据导入 sometable 表,可以直接通过管道将该文件传给 clickhouse-client:
clickhouse-client -q "INSERT INTO sometable FORMAT CSV" < data_small.csv请注意,我们使用 FORMAT CSV 告诉 ClickHouse 当前正在摄取 CSV 格式的数据。或者,也可以使用 FROM INFILE 子句从本地文件加载数据:
INSERT INTO sometable
FROM INFILE 'data_small.csv'
FORMAT CSV这里我们使用 FORMAT CSV 子句,让 ClickHouse 能识别文件格式。我们也可以使用 url() 函数直接从 URL 加载数据,或者使用 s3() 函数从 S3 文件加载数据。
带表头的 CSV 文件
假设我们的 CSV 文件带有表头:
head data-small-headers.csv"path","month","hits"
"Akiba_Hebrew_Academy","2017-08-01",241
"Aegithina_tiphia","2018-02-01",34要从该文件导入数据,可以使用 CSVWithNames 格式:
clickhouse-client -q "INSERT INTO sometable FORMAT CSVWithNames" < data_small_headers.csv在这种情况下,ClickHouse 在从文件导入数据时会跳过第一行。
使用自定义分隔符的 CSV 文件
如果 CSV 文件使用的分隔符不是逗号,可以使用 format_csv_delimiter 选项来设置对应的分隔符:
SET format_csv_delimiter = ';'现在,从 CSV 文件导入时,将使用 ; 作为分隔符,而不是逗号。
跳过 CSV 文件中的行
有时,在从 CSV 文件导入数据时,我们可能需要跳过前几行。这可以通过使用 input_format_csv_skip_first_lines 选项来实现:
SET input_format_csv_skip_first_lines = 10在这种情况下,我们将跳过 CSV 文件的前十行:
SELECT count(*) FROM file('data-small.csv', CSV)┌─count()─┐
│ 990 │
└─────────┘该file有 1k 行,但由于我们要求跳过前 10 行,因此 ClickHouse 只加载了 990 行。
在 CSV 文件中处理 NULL 值
NULL 值的编码方式可能因生成该文件的应用程序不同而有所差异。默认情况下,ClickHouse 在 CSV 中使用 \N 表示 NULL 值。不过,我们可以通过 format_csv_null_representation 选项来修改这种表示方式。
假设我们有以下 CSV 文件:
> cat nulls.csv
Donald,90
Joe,Nothing
Nothing,70如果从该文件加载数据,ClickHouse 会将 Nothing 视为 String (这是正确的) :
SELECT * FROM file('nulls.csv')┌─c1──────┬─c2──────┐
│ Donald │ 90 │
│ Joe │ Nothing │
│ Nothing │ 70 │
└─────────┴─────────┘如果希望 ClickHouse 将 Nothing 视为 NULL,可以通过以下选项进行设置:
SET format_csv_null_representation = 'Nothing'现在,NULL 已出现在我们预期的位置:
SELECT * FROM file('nulls.csv')┌─c1─────┬─c2───┐
│ Donald │ 90 │
│ Joe │ ᴺᵁᴸᴸ │
│ ᴺᵁᴸᴸ │ 70 │
└────────┴──────┘TSV (制表符分隔) 文件
制表符分隔格式广泛用作数据交换格式。要将 TSV 文件 中的数据加载到 ClickHouse,请使用 TabSeparated 格式:
clickhouse-client -q "INSERT INTO sometable FORMAT TabSeparated" < data_small.tsv此外,还有一种 TabSeparatedWithNames 格式,可用于处理带表头的 TSV 文件。此外,与 CSV 类似,我们也可以使用 input_format_tsv_skip_first_lines 选项跳过前 X 行。
原始 TSV
有时,TSV 文件保存时不会对制表符和换行符进行转义。我们应使用 TabSeparatedRaw 来处理这类文件。
导出为 CSV
前面示例中的任何格式也都可以用于导出数据。要将表中的数据 (或查询结果) 导出为 CSV 格式,我们使用相同的 FORMAT 子句:
SELECT *
FROM sometable
LIMIT 5
FORMAT CSV"Akiba_Hebrew_Academy","2017-08-01",241
"Aegithina_tiphia","2018-02-01",34
"1971-72_Utah_Stars_season","2016-10-01",1
"2015_UEFA_European_Under-21_Championship_qualification_Group_8","2015-12-01",73
"2016_Greater_Western_Sydney_Giants_season","2017-05-01",86要为 CSV 文件添加表头,可使用 CSVWithNames 格式:
SELECT *
FROM sometable
LIMIT 5
FORMAT CSVWithNames"path","month","hits"
"Akiba_Hebrew_Academy","2017-08-01",241
"Aegithina_tiphia","2018-02-01",34
"1971-72_Utah_Stars_season","2016-10-01",1
"2015_UEFA_European_Under-21_Championship_qualification_Group_8","2015-12-01",73
"2016_Greater_Western_Sydney_Giants_season","2017-05-01",86将导出数据保存为 CSV 文件
要将导出数据保存到文件中,可以使用 INTO…OUTFILE 子句:
SELECT *
FROM sometable
INTO OUTFILE 'out.csv'
FORMAT CSVWithNames36838935 rows in set. Elapsed: 1.304 sec. Processed 36.84 million rows, 1.42 GB (28.24 million rows/s., 1.09 GB/s.)请注意,ClickHouse 仅用 ~1 秒就将 3600 万行数据保存到 CSV 文件中。
使用自定义分隔符导出 CSV
如果要使用逗号以外的分隔符,可以通过 format_csv_delimiter 设置选项来指定:
SET format_csv_delimiter = '|'现在,ClickHouse 将使用 | 作为 CSV 格式的分隔符:
SELECT *
FROM sometable
LIMIT 5
FORMAT CSV"Akiba_Hebrew_Academy"|"2017-08-01"|241
"Aegithina_tiphia"|"2018-02-01"|34
"1971-72_Utah_Stars_season"|"2016-10-01"|1
"2015_UEFA_European_Under-21_Championship_qualification_Group_8"|"2015-12-01"|73
"2016_Greater_Western_Sydney_Giants_season"|"2017-05-01"|86为 Windows 导出 CSV
如果希望 CSV 文件在 Windows 环境中正常使用,建议启用 output_format_csv_crlf_end_of_line 选项。这样会使用 \r\n 作为换行符,而不是 \n:
SET output_format_csv_crlf_end_of_line = 1;CSV 文件的 schema 推断
在很多情况下,我们会处理结构未知的 CSV 文件,因此需要先确定各列应使用哪些类型。默认情况下,ClickHouse 会根据对给定 CSV 文件的分析尝试推断数据格式。这称为 "Schema Inference"。可以结合 file() 函数,使用 DESCRIBE 语句查看检测到的数据类型:
DESCRIBE file('data-small.csv', CSV)┌─name─┬─type─────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Nullable(String) │ │ │ │ │ │
│ c2 │ Nullable(Date) │ │ │ │ │ │
│ c3 │ Nullable(Int64) │ │ │ │ │ │
└──────┴──────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘这里,ClickHouse 可以高效地自动推断 CSV 文件的列类型。如果不想让 ClickHouse 自动推断,可以通过以下选项将其禁用:
SET input_format_csv_use_best_effort_in_schema_inference = 0在这种情况下,所有列类型都会被视为 String。
使用显式列类型导出和导入 CSV
ClickHouse 还支持在使用 CSVWithNamesAndTypes (以及其他 *WithNames 格式家族) 导出数据时,显式指定列类型:
SELECT *
FROM sometable
LIMIT 5
FORMAT CSVWithNamesAndTypes"path","month","hits"
"String","Date","UInt32"
"Akiba_Hebrew_Academy","2017-08-01",241
"Aegithina_tiphia","2018-02-01",34
"1971-72_Utah_Stars_season","2016-10-01",1
"2015_UEFA_European_Under-21_Championship_qualification_Group_8","2015-12-01",73
"2016_Greater_Western_Sydney_Giants_season","2017-05-01",86这种格式会包含两行表头——一行是列名,另一行是列类型。这样 ClickHouse (以及其他应用) 在从此类文件加载数据时,就能识别列类型:
DESCRIBE file('data_csv_types.csv', CSVWithNamesAndTypes)┌─name──┬─type───┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ path │ String │ │ │ │ │ │
│ month │ Date │ │ │ │ │ │
│ hits │ UInt32 │ │ │ │ │ │
└───────┴────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘现在 ClickHouse 不再靠猜测,而是根据 (第二行) 表头来识别列类型。
自定义分隔符、分隔符和转义规则
在较复杂的场景中,文本数据虽然可以采用高度自定义的格式,但仍然具有一定的结构。对于这类情况,ClickHouse 提供了专门的 CustomSeparated 格式,支持自定义转义规则、分隔符、行分隔符以及起始/结束符号。
假设文件中有以下数据:
row('Akiba_Hebrew_Academy';'2017-08-01';241),row('Aegithina_tiphia';'2018-02-01';34),...可以看到,每一行都包裹在 row() 中,行与行之间用 , 分隔,而各个值则用 ; 分隔。在这种情况下,我们可以使用以下设置从该文件中读取数据:
SET format_custom_row_before_delimiter = 'row(';
SET format_custom_row_after_delimiter = ')';
SET format_custom_field_delimiter = ';';
SET format_custom_row_between_delimiter = ',';
SET format_custom_escaping_rule = 'Quoted';现在我们可以从这个自定义格式的file中加载数据:
SELECT *
FROM file('data_small_custom.txt', CustomSeparated)
LIMIT 3┌─c1────────────────────────┬─────────c2─┬──c3─┐
│ Akiba_Hebrew_Academy │ 2017-08-01 │ 241 │
│ Aegithina_tiphia │ 2018-02-01 │ 34 │
│ 1971-72_Utah_Stars_season │ 2016-10-01 │ 1 │
└───────────────────────────┴────────────┴─────┘我们还可以使用 CustomSeparatedWithNames 来正确导出和导入表头。要处理更复杂的情况,还可以了解 regex 和 Template 格式。
处理大型 CSV 文件
CSV 文件可能很大,而 ClickHouse 能高效处理各种大小的文件。大型文件通常是压缩格式,ClickHouse 原生支持这一点,因此处理前无需先解压。我们可以在插入时使用 COMPRESSION 子句:
INSERT INTO sometable
FROM INFILE 'data_csv.csv.gz'
COMPRESSION 'gzip' FORMAT CSV如果省略 COMPRESSION 子句,ClickHouse 仍会根据文件扩展名尝试推断压缩方式。也可以用同样的方法将文件直接导出为压缩格式:
SELECT *
FROM for_csv
INTO OUTFILE 'data_csv.csv.gz'
COMPRESSION 'gzip' FORMAT CSV这将创建一个压缩的 data_csv.csv.gz 文件。
其他格式
ClickHouse 支持多种格式,包括文本格式和二进制格式,以适配各种场景和平台。你可以在以下文章中了解更多格式及其使用方式:
- CSV 和 TSV 格式
- Parquet
- JSON 格式
- Regex 和 Template
- 原生格式和二进制格式
- SQL 格式
另请参阅 clickhouse-local——这是一款便携且功能完备的工具,无需 ClickHouse server 即可处理本地和远程文件。