事实核验:2026-08-16 · 状态:维护中

记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。

warning 适用范围与待维护事项

本文适用于已安装 DuckDB、具备基本 SQL 阅读能力且对当前工作目录有写权限的本地文件练习。第1节生成的数据可用于第2、4、5节;第3节的 lake/orders 路径与 Hive 分区目录仅为结构示意,需要自行准备匹配文件后执行。列裁剪、过滤下推和分区裁剪的实际效果应以执行计划确认,不代表生产性能保证。本轮未执行 SQL 或重新核验版本,原 last_verified 不代表本轮验证。

DuckDB直接查询文件与SQL实践

abstract 学习目标

不先导入数据库,直接用 SQL 查询 CSV、Parquet 和一组分区文件;理解自动推断、显式类型、列裁剪、过滤下推和 COPY 导出各自解决什么问题。

DuckDB 的高价值入口不是“先建表”,而是把文件当表查询。它适合一次性探索、质量检查和本地数据集市的入口层。开始前可先看 DuckDB学习路线图 与 SQL学习路线图。

1. 从一个可复现数据集开始

CREATE TABLE orders AS
SELECT * FROM (VALUES
  (1, DATE '2026-08-01', 'paid', 99.00),
  (2, DATE '2026-08-01', 'cancelled', 30.00),
  (3, DATE '2026-08-02', 'paid', 120.00)
) AS t(order_id, pay_date, status, amount);

COPY orders TO 'orders.parquet' (FORMAT parquet);
COPY orders TO 'orders.csv' (HEADER, DELIMITER ',');

预期结果:当前目录生成两个文件,各有 3 行数据。这个样例避免依赖外部下载,第2、4、5节 SQL 可使用这两个文件执行;第3节的多文件与分区路径为示意,需自行准备匹配目录和文件。

2. 直接查询 CSV 与 Parquet

文件路径可以直接出现在 FROM 中;需要更多读取参数时,使用表函数。

SELECT pay_date, SUM(amount) AS gmv
FROM 'orders.parquet'
WHERE status = 'paid'
GROUP BY pay_date
ORDER BY pay_date;

预期得到 2026-08-01 → 99.00、2026-08-02 → 120.00。Parquet 查询会自动做列裁剪,并在可行时把过滤条件下推到扫描层,因此不要习惯性先 SELECT * 再交给 Python 过滤。

CSV 依赖采样推断。生产脚本应在日期、金额、前导零编码等关键字段上显式声明类型:

SELECT *
FROM read_csv(
  'orders.csv',
  header = true,
  columns = {
    'order_id': 'BIGINT',
    'pay_date': 'DATE',
    'status': 'VARCHAR',
    'amount': 'DECIMAL(18,2)'
  }
);

warning 自动推断适合探索,不是长期数据契约。后续批次出现空值、异常日期或超长编码时,推断结果可能变化;金额也不应无条件使用浮点数。

3. 多文件与分区目录

DuckDB 表函数接受 glob。filename = true 能保留来源文件,便于审计坏批次;结构随批次新增列时可评估 union_by_name = true。

SELECT filename, status, COUNT(*) AS rows
FROM read_parquet('lake/orders/**/*.parquet', filename = true)
GROUP BY filename, status;

Hive 风格路径如 dt=2026-08-01/region=cn/part.parquet 在启用 Hive 分区识别时可把目录值作为分区列。对分区列 dt 添加可用于裁剪的过滤条件,可以减少需要扫描的文件;实际裁剪效果应通过执行计划确认。不要把成千上万个微小文件当成“天然并行”:文件打开与元数据处理也有成本。

4. 用 SQL 做一次完整分析

WITH paid AS (
  SELECT pay_date, order_id, amount
  FROM read_parquet('orders.parquet')
  WHERE status = 'paid'
), daily AS (
  SELECT pay_date,
         COUNT(DISTINCT order_id) AS orders,
         SUM(amount) AS gmv
  FROM paid
  GROUP BY pay_date
)
SELECT *, gmv / NULLIF(orders, 0) AS avg_order_value
FROM daily
ORDER BY pay_date;

这段 SQL 把读取、过滤、聚合和指标计算放在一次查询中。复杂业务口径可参考 AI时代分析SQL实战;输出前先验证行数、唯一键、空值和金额范围。

5. 导出可复用结果

COPY (
  SELECT * FROM 'orders.parquet' WHERE status = 'paid'
) TO 'paid_orders.parquet' (FORMAT parquet, COMPRESSION zstd);

COPY 适合把查询结果固化为下游文件。边界是:写出成功不等于数据正确;应同时记录源文件范围、SQL 版本、结果行数和校验摘要。若需要进入 Python、Arrow 或 Polars,继续阅读 Python集成与数据交换。

6. 常见误区与边界

  • DuckDB 是单机嵌入式分析引擎,不是高并发交易数据库。
  • CSV 没有可靠 schema,关键任务要显式指定类型与日期格式。
  • 直接查文件不等于没有治理:路径、分区、命名和口径仍需稳定。
  • LIMIT 10 只能看样例,不能替代完整质量校验。
  • 远程对象存储还涉及凭据、网络重试与缓存,本篇只覆盖本地文件。

7. 练习

  1. 增加一条 amount IS NULL 的记录,比较 SUM、COUNT(*) 与 COUNT(amount)。
  2. 创建两个列顺序不同的 Parquet 文件,测试按名称合并并核对 schema。
  3. 用 EXPLAIN 查看按状态过滤时的扫描计划,确认只读取所需列。
  4. 导出每日 GMV,写出行数、日期范围与 GMV 合计三个验收断言。

8. 延伸阅读

官方来源