导入与导出
本节目标
读完本篇,你应能:
- 用
read_csv、路径字面量或COPY ... FROM读入 CSV,并在需要时显式指定分隔符、表头与列类型 - 用
read_parquet(或'*.parquet'路径)读 Parquet,并用DESCRIBE查看列类型 - 用
read_json/read_json_auto把 JSON 读成表,或用COPY ... FROM导入 JSON - 用
COPY ... TO把查询结果或表导出为 CSV、Parquet、JSON - 用 glob 通配符一次读多个同类型文件,并了解 Hive 分区目录的读写法
- 避开 auto-detect 类型推断、编码、大文件内存 等常见误区
若还不熟悉 CAST / TRY_CAST 与嵌套类型,请先阅读 类型与函数。
概念:文件即表
DuckDB 把磁盘上的数据文件当作外部表 (external table):在 SELECT 里写文件路径,引擎按需扫描,不必先把整文件导入数据库。路径相对于当前工作目录(CLI 启动时的目录);示例里用占位路径如 'data/flights.csv',你需要在本地准备同名文件或改成自己的路径。
两种等价风格很常见:
| 风格 | 示例 | 适用 |
|---|---|---|
| 路径字面量 | SELECT * FROM 'data/flights.csv' | 扩展名明确、选项可自动推断 |
| 读函数 | read_csv('data/flights.csv', ...) | 需要显式选项或非标准扩展名 |
持久化到 .duckdb 里用 CREATE TABLE ... AS SELECT 或 COPY table FROM 'file',适合反复查询同一批数据。
下面示例建议在 CLI 中按块复制执行;若文件不存在,先创建 data/ 目录并放入自己的 CSV / Parquet / JSON,或跳过该块。
CSV:read_csv 与 COPY ... FROM
自动推断(分隔符、表头、类型):
SELECT * FROM 'data/flights.csv' LIMIT 10;
显式选项(脏数据、非常规分隔符、强制列类型):
SELECT *
FROM read_csv(
'data/flights.csv',
delim = '|',
header = true,
columns = {
'FlightDate': 'DATE',
'UniqueCarrier': 'VARCHAR',
'OriginCityName': 'VARCHAR',
'DestCityName': 'VARCHAR'
}
)
LIMIT 10;
导入到已有表结构(列名、顺序需与表一致,或用 INSERT 子查询映射):
CREATE OR REPLACE TABLE flights AS
SELECT * FROM read_csv('data/flights.csv', header = true);
-- 或追加到已有表
-- COPY flights FROM 'data/flights.csv';
导出 CSV:
COPY (SELECT * FROM flights WHERE DestCityName = 'Chicago')
TO 'data/out/chicago_flights.csv' (HEADER, DELIMITER ',');
COPY ... TO 会创建目标文件(及缺失的目录,视版本与选项而定);覆盖前请确认路径,避免误删生产数据。
Parquet:read_parquet
Parquet 带列类型与压缩,分析场景通常比 CSV 更省 I/O。单文件:
SELECT * FROM 'data/metrics.parquet' LIMIT 10;
DESCRIBE SELECT * FROM 'data/metrics.parquet';
扩展名不是 .parquet 时用函数:
SELECT * FROM read_parquet('data/metrics.pq');
物化到表:
CREATE OR REPLACE TABLE metrics AS
SELECT * FROM read_parquet('data/metrics.parquet');
导出 Parquet(推荐做中间结果或与其他系统交换):
COPY metrics TO 'data/export/metrics.parquet' (FORMAT parquet);
Parquet 的列裁剪与谓词下推由引擎自动利用;只 SELECT 需要的列、加上 WHERE,往往比 SELECT * 全表扫更省内存与时间。
JSON:read_json 与 COPY
JSON 常用于 API 或日志;DuckDB 通过 json 扩展(多数发行版首次使用时会自动加载)提供读入能力。每行一个 JSON 对象(NDJSON)或数组包对象都常见,具体以文件形状为准。
自动推断结构:
SELECT * FROM read_json_auto('data/events.json') LIMIT 10;
显式格式(与官方文档选项一致,如 format、columns 等):
SELECT * FROM read_json(
'data/events.json',
format = 'auto'
)
LIMIT 10;
导入表:
COPY events FROM 'data/events.json' (FORMAT json, AUTO_DETECT true);
导出 JSON:
COPY (SELECT id, payload FROM events LIMIT 100)
TO 'data/export/events.json' (FORMAT json, ARRAY true);
嵌套字段后续可用 -> / json_extract 与 类型与函数 中的 STRUCT 思路配合;本篇只覆盖「读成表 / 写出文件」。
多文件通配符
一次读目录下多个同类型文件,路径使用 glob:
-- 某目录下所有 CSV
SELECT * FROM 'data/inbox/*.csv' LIMIT 10;
-- 任意子目录(递归)
SELECT * FROM 'data/lake/**/*.parquet' LIMIT 10;
-- 显式函数 + 文件列表
SELECT * FROM read_parquet(['data/part-000.parquet', 'data/part-001.parquet']);
多个文件 schema 不一致(列名、类型不同)时,合并可能报错或产生 NULL 填充;需要先统一列名、用 union_by_name 等选项,或分文件清洗。详见官方 Combining Schemas。
通配符匹配到的文件过多时,先用 LIMIT 或 WHERE 在小范围验证,再跑全量。
Hive 分区:读与写入门
Hive 风格把分区列编进目录名,例如 year=2026/month=10/data.parquet。读 Parquet 数据集时打开 hive_partitioning,分区列会从路径解析并出现在结果中:
SELECT *
FROM read_parquet('data/orders/*/*/*.parquet', hive_partitioning = true)
LIMIT 10;
写出带分区目录的 Parquet(PARTITION_BY 只能是列名,不能写表达式;需要先在子查询里算好列):
COPY (
SELECT
*,
year(order_ts) AS year,
month(order_ts) AS month
FROM orders_raw
)
TO 'data/orders_hive' (FORMAT parquet, PARTITION_BY (year, month));
读回时同样使用 hive_partitioning = true。分区列与文件内列重名时的行为以官方 Hive Partitioning 为准。
常见坑
- 类型 auto-detect:CSV/JSON 常靠采样行推断类型;ID 前导零、混合数字与文本、日期格式不统一会被推成
VARCHAR或推断错误。生产导入应用columns = {...}、types或先CREATE TABLE固定 schema,必要时用TRY_CAST清洗(见 类型与函数)。 - 编码:默认按 UTF-8 读 CSV;Windows 导出的 GBK/GB18030 等若仍按 UTF-8 读会出现乱码或解析失败。应在
read_csv中指定与文件一致的编码(如encoding = 'GB18030'),或先在外部转成 UTF-8 再读;不确定时用十六进制编辑器或小样本试读。 - 大文件与内存:
SELECT * FROM 'data/huge.csv'仍可能扫描全文件;探索阶段务必加LIMIT,导出/分析优先 Parquet,需要重复查询时用CREATE TABLE ... AS SELECT、COPY ... FROM或INSERT物化。内存不足时可调低并发、只选必要列,或在官方文档中查阅memory_limit/threads等设置(嵌入式与 CLI 行为略有差异)。 - 路径与工作目录:相对路径随 CLI 启动目录变化;脚本里建议用绝对路径或启动前
cd到项目根。通配符在部分环境下不展开 shell 元字符,应写在 SQL 字符串里由 DuckDB 解析。 - COPY 与表结构:
COPY table FROM 'file'要求与表列兼容;列序、分隔符、有无表头不一致会导致报错或静默错位。先DESCRIBE table,对小文件试COPY或INSERT BY NAME(若版本支持)。 - 多文件 schema:批量
*.csv时各文件列集不同会失败;先DESCRIBE SELECT * FROM 'data/*.csv' LIMIT 0或单文件检查,再决定union_by_name或 ETL 统一。
延伸阅读
- CSV Import · Parquet · JSON
- COPY Statement —
FROM/TO与格式选项 - Reading Multiple Files · Hive Partitioning
- 系列索引:DuckDB 教程
- 下一篇:Python 客户端 — 连接、Pandas/Arrow 与 Relational API
上一篇:类型与函数 · 下一篇:Python 客户端