日常工作中,Excel 用来保存和传递数据非常常见。
但 Excel 与数据库之间的交互,很多时候并不友好。
原因也很简单:Excel 往往不是一张干净的数据表。
比如:
#N/A#DIV/0!#VALUE!#REF!这些内容在 Excel 里可能只是“不太好看”,但一旦导入数据库,就很容易导致异常,甚至直接导入失败。
所以很多时候,真正耗费时间的不是分析数据,而是在分析之前整理数据,先做一轮手工整理:
删除多余表头、处理合并单元格、清理异常值、另存为 CSV,然后再导入数据库。
这一步很烦,尤其是周期性报表。
格式基本固定,数据量也不大,但每周、每月都要重复处理一次。
最近几天在尝试 DuckDB 的 Excel 扩展。
最开始只是想看看它能不能直接读取 Excel 文件,结果用了几天之后发现:
它解决的并不仅仅是“读取 Excel”的问题。
更重要的是,它把 Excel 和数据库之间那层麻烦的中间步骤省掉了。
一个 read_xlsx() 函数,就可以比较轻松地应对指定 Sheet、指定区域、跳过无效表头、处理脏数据等常见问题。
对于一些固定格式、周期性生成的小数据量报表,甚至可以不用先入库。
直接读取 Excel,直接用 SQL 处理。
后续收到新的 Excel 文件时,只需要在脚本里改一下文件名,就能一键输出整理结果。
这个是使用过程中,觉得最舒服的地方。
本文就从基础安装、常用参数和几个实际场景出发,记录一下 DuckDB 在处理 Excel 文件时的一些使用技巧。
.xlsx,不支持 **.xls**。如果是老式 .xls 文件,需要先另存为 .xlsx。LOAD excel;INSTALL excel;LOAD excel;SELECT * FROM'F:\测试中转数据\测试数据-20260608.xlsx'
read_xlsxread_xlsx 是读取 Excel 内容时最常用、最可控的方法。它支持指定工作表、表头、读取范围、错误处理和类型控制。--这个示例展示的是读取无表头数据时,如何指定表头SELECTA1 AS 月份,B1 AS'公司1',C1 AS'公司2',D1 AS'公司3'FROM read_xlsx('F:\测试中转数据\测试数据-20260608.xlsx', header = false, sheet = '无表头', all_varchar = true, ignore_errors = true);
SELECT* FROM read_xlsx('F:\测试中转数据\测试数据-20260608.xlsx', header = true, sheet = '有表头');
SELECT * FROM read_xlsx('F:\测试中转数据\测试数据-20260608.xlsx',range = 'A3:D9999', header = true, sheet = '有表头', stop_at_empty = true);
SELECT * FROM read_xlsx('F:\测试中转数据\测试数据-20260608.xlsx',range = 'A3:D9999', header = true, sheet = '有表头', stop_at_empty = true, all_varchar = true);
ignore_errors = true 会把这些异常值替换为 NULL,避免整个读取过程失败。all_varchar = true时,可以规避很多脏数据的影响了,遇到特殊情况,可以再加上ignore_errors = true,SELECT * FROM read_xlsx('F:\测试中转数据\测试数据-20260608.xlsx',range = 'A3:D9999', header = true, sheet = '有表头', stop_at_empty = true, all_varchar = true, ignore_errors = true);header | |||
sheet | |||
all_varchar | |||
ignore_errors | |||
range | A1:D100 | ||
stop_at_empty | |||
empty_as_varchar |
最常用参数是 header、sheet、range、all_varchar、ignore_errors。
其中 all_varchar = true 是处理复杂 Excel 文件时非常实用的稳定读取方式。
通过使用read_xlsx可以让Excel与数据库的交互更加方便优雅
最大的价值并不仅仅是 DuckDB 能读取 Excel。
而且还可以把那些每日、每周、每月等,重复整理 Excel 的工作,
变成一个可以复用执行的 SQL 流程。