导入与导出

本节目标

读完本篇,你应能:

  • 用 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 为准。

常见坑

  1. 类型 auto-detect:CSV/JSON 常靠采样行推断类型;ID 前导零、混合数字与文本、日期格式不统一会被推成 VARCHAR 或推断错误。生产导入应用 columns = {...}、types 或先 CREATE TABLE 固定 schema,必要时用 TRY_CAST 清洗(见 类型与函数)。
  2. 编码:默认按 UTF-8 读 CSV;Windows 导出的 GBK/GB18030 等若仍按 UTF-8 读会出现乱码或解析失败。应在 read_csv 中指定与文件一致的编码(如 encoding = 'GB18030'),或先在外部转成 UTF-8 再读;不确定时用十六进制编辑器或小样本试读。
  3. 大文件与内存:SELECT * FROM 'data/huge.csv' 仍可能扫描全文件;探索阶段务必加 LIMIT,导出/分析优先 Parquet,需要重复查询时用 CREATE TABLE ... AS SELECT、COPY ... FROM 或 INSERT 物化。内存不足时可调低并发、只选必要列,或在官方文档中查阅 memory_limit / threads 等设置(嵌入式与 CLI 行为略有差异)。
  4. 路径与工作目录:相对路径随 CLI 启动目录变化;脚本里建议用绝对路径或启动前 cd 到项目根。通配符在部分环境下不展开 shell 元字符,应写在 SQL 字符串里由 DuckDB 解析。
  5. COPY 与表结构:COPY table FROM 'file' 要求与表列兼容;列序、分隔符、有无表头不一致会导致报错或静默错位。先 DESCRIBE table,对小文件试 COPY 或 INSERT BY NAME(若版本支持)。
  6. 多文件 schema:批量 *.csv 时各文件列集不同会失败;先 DESCRIBE SELECT * FROM 'data/*.csv' LIMIT 0 或单文件检查,再决定 union_by_name 或 ETL 统一。

延伸阅读


上一篇:类型与函数 · 下一篇:Python 客户端