乐于分享
好东西不私藏

Python教程丨没安装数据库也能运行SQL语句?试试DuckDB!

Python教程丨没安装数据库也能运行SQL语句?试试DuckDB!

DuckDB :一款文件分析引擎

在日常的数据分析工作中,我们经常会陷入一种尴尬的境地:手头攒着几份 CSV、Excel 或 Parquet 文件,只想快速探查一下数据、做个简单的分组聚合。此时,特意去搭建一套重量级的数据库服务(如 MYSQL)未免大材小用;而如果直接用 Pandas,又免不了要写一堆读取、转换、合并的样板代码,略显繁琐。

DuckDB 正是为了解决这种“轻量、快速、即席分析”的痛点而生的。 

简单来说,DuckDB 是一款嵌入式的、专注于分析型(OLAP)查询的列式数据库。

它最核心的魅力,在于打破了“数据必须先导入数据库才能查”的传统逻辑,允许你直接用 SQL 去查本地文件

它最适合解决的是“我手里已经有几份 CSV 和 Excel,只想先查清楚答案”这类问题。先把文件当表查,结果对了,再决定要不要进更重的数据处理流程。

它和 SQLite 也不一样。SQLite 更像通用嵌入式数据库,DuckDB 更偏分析查询,对聚合、关联和列式处理更顺手。

DuckDB 直接对 CSV 和 Excel 文件执行查询,不需要先把数据搬进复杂流程。

许多本地数据任务,实际上并不需要一上来就构建繁琐的数据处理流程。

手里有几份 CSV,想按区域汇总一下销售额;旁边还有一份退款表,想找出退款订单;库存信息在 Excel 里,想筛出低库存商品。

这些活如果直接上 pandas,当然能做。

但在实际操作中,为了获得一个简单的汇总结果,开发者往往需要编写大量读取文件、转换类型、合并表格及筛选字段的样板代码。

DuckDB 适合换一种方式。

把本地 CSV 和 Excel 当成临时数据表,直接用 SQL 查。

不需要搭建复杂的数据库环境,也无需将数据导入 SQLite,甚至不用将处理步骤拆解为繁杂的 DataFrame。你只需要明确查询逻辑,用 SQL 获取结果,再决定是否进行后续数据加工。


举个例子

我们有一些数据放在:

.cache/topic-139-duckdb-query-files/

里面有三份数据:

data/orders.csv      订单表data/refunds.csv     退款表data/inventory.xlsx  库存表

我们要完成三件事:

  1. 1. 按区域汇总已付款订单金额
  2. 2. 把订单表和退款表关联起来,找出退款订单
  3. 3. 从 Excel 库存表里筛出低库存商品

跑通后,终端输出大概是这样:

各区域已付款销售额  华东 | 1348.4 | 3  华南 | 625.0 | 2  华北 | 128.0 | 1退款订单  1003 | 北城工作室 | 520.0  1005 | 西岭设计 | 0.0

项目已经验证过:

PYTHONPATH=. pytest -q

结果是:

3 passed
本地文件先被当成临时数据表查询,再决定是否进入更重的数据处理链路。

安装依赖与准备工作

这个示例只需要三个依赖:

duckdb>=1.4.0openpyxl>=3.1.0pytest>=8.0.0
从官方的介绍中我们也可以看到依赖的安装的引入也非常简单,只需要一行即可。

可以这样准备环境:

python -m venv .venv. .venv/bin/activatepip install -r requirements.txt

Excel 样例文件由脚本生成:

python scripts/create_sample_xlsx.py

如果仅需对 CSV 文件进行查询,可以跳过 Excel 相关配置:

python query_files.py --skip-excel

基于 SQL 执行单表 CSV 查询

DuckDB 的 Python 用法很简单:

import duckdbconnection = duckdb.connect(database=":memory:")

按区域汇总销售额的函数是这样:

def sales_by_region(connection: duckdb.DuckDBPyConnection) -> list[tuple[str, float, int]]:    query = """        SELECT            region,            round(sum(amount), 2) AS total_amount,            count(*) AS order_count        FROM read_csv($orders, header = true)        WHERE status = 'paid'        GROUP BY region        ORDER BY total_amount DESC    """    return connection.execute(query, {"orders": str(DATA_DIR / "orders.csv")}).fetchall()

这段代码里有两个点很实用。

第一,read_csv($orders, header = true) 直接读取 CSV 文件,省去了将数据导入临时表的步骤。

第二,文件路径通过参数传进去,不把路径直接拼进 SQL 字符串里。

SQL 本身也很清楚:

  • • WHERE status = 'paid' 只看已付款订单
  • • GROUP BY region 按区域分组
  • • sum(amount) 计算销售额
  • • count(*) 统计订单数

如果对 SQL 比较熟悉,这种方式比编写一长串链式的 DataFrame 接口更加直观。


跨文件 CSV 关联查询设计

本地文件查询最常见的需求,是把两张表串起来。

比如订单表里有客户和订单金额,退款表里有退款原因和退款金额。想找出退款订单,不需要在本地将两份 CSV 手动合并。

直接 JOIN

def refunded_orders(connection: duckdb.DuckDBPyConnection) -> list[tuple[int, str, float]]:    query = """        SELECT            orders.order_id,            orders.customer,            refunds.refund_amount        FROM read_csv($orders, header = true) AS orders        JOIN read_csv($refunds, header = true) AS refunds        USING (order_id)        ORDER BY refunds.refund_amount DESC    """    return connection.execute(        query,        {            "orders": str(DATA_DIR / "orders.csv"),            "refunds": str(DATA_DIR / "refunds.csv"),        },    ).fetchall()

这就是 DuckDB 适合本地文件任务的地方。

文件还是文件,但查询时可以像数据库表一样使用。

如果只是临时排查问题,跑一条 SQL 就够了。后面如果要变成长期工具,再把 SQL 固定进脚本和测试。


使用 read_xlsx 查询 Excel 文件

DuckDB 也可以查 Excel。

不过,Excel 读取依赖内置的 excel 扩展。需要在脚本中进行安装与加载:

connection.execute("INSTALL excel")connection.execute("LOAD excel")

然后用 read_xlsx 读取指定 sheet:

def low_stock_items(connection: duckdb.DuckDBPyConnection) -> list[tuple[str, str, int, int]]:    connection.execute("INSTALL excel")    connection.execute("LOAD excel")    query = """        SELECT            sku,            name,            stock,            safe_stock        FROM read_xlsx($inventory, sheet = 'stock')        WHERE stock < safe_stock        ORDER BY stock ASC    """    return connection.execute(query, {"inventory": str(DATA_DIR / "inventory.xlsx")}).fetchall()

这段查询做的事很简单:

  • • 从 inventory.xlsx 的 stock sheet 读取库存
  • • 找出 stock < safe_stock 的商品
  • • 按当前库存从低到高排序

需要注意一点:INSTALL excel 可能需要能访问 DuckDB 的扩展仓库。

如果目标机器处于离线环境或网络受限,应当提前部署扩展,或者将 Excel 转换为 CSV。在编写工具时,应避免盲目假定网络扩展随时可用。


DuckDB 的适用边界

在以下场景中,推荐优先选用 DuckDB:

  • • 文件是 CSV、Excel、Parquet 这类表格数据
  • • 目标是筛选、分组、排序、关联、汇总
  • • 知道自己想查什么数据
  • • 后面可能会扩展成脚本、报告或自动化流程

如果只是打开表格改几个单元格,Excel 本身就够了。

如果要做复杂数据深加工,不妨试试DuckDB,说不定能帮助我们节省大量时间。