DuckDB数据分析实战:读写Excel文件

在企业数据分析场景中,Excel 几乎无处不在。为此,DuckDB 提供了专门的插件,让数据分析师和开发人员可以像查询数据库表一样直接查询 Excel 文件,同时支持将分析结果直接导出为 Excel 文件。

安装插件

DuckDB 官方提供的 Excel 插件(excel)在第一次使用时可以自动安装并加载,也可以手动执行以下命令:

sql 复制代码
INSTALL excel;
LOAD excel;

然后我们准备一个 Excel 示例文件(fruits.xlsx),内容如下:

excel 插件支持 .xlsx 格式文件,但不支持 .xls 格式文件。

读取Excel

DuckDB 可以将 Excel 文件当作一个数据源进行查询,例如:

sql 复制代码
SELECT *
FROM 'fruits.xlsx';

┌─────────┬────────┬────────┐
│  name   │ price  │  num   │
│ varchar │ double │ double │
├─────────┼────────┼────────┤
│ 苹果    │    9.9 │   20.0 │
│ 西瓜    │    2.5 │   15.0 │
│ 梨      │    6.6 │    6.0 │
└─────────┴────────┴────────┘

DuckDB 能够基于文件后缀调用相应的处理函数,对于 XLSX 文件实际调用的是 read_xlsx 函数。因此,以上查询也可以写成下面的语句:

sql 复制代码
SELECT *
FROM read_xlsx('fruits.xlsx', header = true);

其中,header = true 表示读取的内容中包含标题(第一行数据)。

read_xlsx 函数的完整参数如下:

参数 类型 默认值 描述
header BOOLEAN 自动推断 是否将第一行当作标题行。
sheet VARCHAR 自动推断 要读取的 sheet 名称。默认读取第一个 sheet。
all_varchar BOOLEAN false 是否将所有单元格当作 VARCHAR 类型读取。
ignore_errors BOOLEAN false 是否忽略错误并且将无法进行类型转换的数据使用 NULL 替代。
range VARCHAR 自动推断 读取的单元格区域。例如,A1:B2表示读取从 A1 到 B2 之间的单元格。如果没有指定该参数,从第一行连续非空单元格开始向下查找,直到遇到第一个空行;列范围由该数据区域决定。
stop_at_empty BOOLEAN 自动推断 遇到空行时是否停止读取。如果指定了range,该参数默认为false,否则默认为true。
empty_as_varchar BOOLEAN false 自动推断类型时,是否将空单元格当作VARCHAR而不是DOUBLE。

例如,以下查询只读取了 fruits.xlsx 文件中的第 2 行数据:

sql 复制代码
SELECT *
FROM read_xlsx('fruits.xlsx', range='A2:C2');

┌─────────┬────────┬────────┐
│    A    │   B    │   C    │
│ varchar │ double │ double │
├─────────┼────────┼────────┤
│ 苹果    │    9.9 │   20.0 │
└─────────┴────────┴────────┘

DuckDB 自动推断出数据中没有标题行,因此生成了默认的标题。

另外,导入 Excel 文件时,DuckDB 会基于单元格内容以及/或者格式自动推断列的类型。主要规则包括:

  • 包含日期格式的数据推断为 DATE;
  • 包含时间格式的数据推断为 TIME;
  • 包含时间戳格式的数据推断为 TIMESTAMP;
  • 包含文本 TRUE/FALSE的数据推断为 BOOLEAN;
  • 空单元格默认推断为 DOUBLE,除非使用 empty_as_varchar 参数进行指定;
  • 数值列推断为 DOUBLE;
  • 文本列推断为 VARCHAR。

导入数据

如果需要在 DuckDB 中存储数据,可以将读取的 Excel 内容写入表中。

第一种方法是使用 CREATE TABLE ... AS 语句创建一个新表,例如:

sql 复制代码
CREATE TABLE fruits AS
SELECT *
FROM read_xlsx('fruits.xlsx');

SELECT *
FROM fruits;

┌─────────┬────────┬────────┐
│  name   │ price  │  num   │
│ varchar │ double │ double │
├─────────┼────────┼────────┤
│ 苹果    │    9.9 │   20.0 │
│ 西瓜    │    2.5 │   15.0 │
│ 梨      │    6.6 │    6.0 │
└─────────┴────────┴────────┘

第二种方法是使用 INSERT INTO 语句将数据导入已有的表,例如:

sql 复制代码
CREATE TABLE fruits2(name VARCHAR, price VARCHAR, cnt DOUBLE);

INSERT INTO fruits2
SELECT *
FROM read_xlsx('fruits.xlsx');

SELECT * FROM fruits2;

┌─────────┬─────────┬────────┐
│  name   │  price  │  cnt   │
│ varchar │ varchar │ double │
├─────────┼─────────┼────────┤
│ 苹果    │ 9.9     │   20.0 │
│ 西瓜    │ 2.5     │   15.0 │
│ 梨      │ 6.6     │    6.0 │
└─────────┴─────────┴────────┘

需要注意,fruits2 的字段类型和 Excel 文件不完全相同,此时 DuckDB 会按照目标表字段类型进行转换。

还有一种导入 Excel 文件的方法是使用 COPY 语句,例如:

sql 复制代码
COPY fruits2 FROM 'fruits.xlsx' (FORMAT xlsx, HEADER);

导出Excel

COPY 语句支持将数据表或者查询结果导出为 Excel 文件(.xlsx),例如:

sql 复制代码
COPY employee TO 'employee.xlsx' (FORMAT xlsx, HEADER true);

以上示例将 employee 中的数据写入名为 employee.xlsx 的文件,写入的第一行为标题。

COPY 导出数据时,还可以指定生成的工作表(sheet)名称。例如:

sql 复制代码
COPY (SELECT * FROM employee WHERE dept_id=5)
TO 'employee.xlsx' (FORMAT xlsx, HEADER true, SHEET 'sales');

由于 Excel 底层只支持数字和字符串两种类型,DuckDB 导出到 Excel 文件时会自动执行类型转换:

  • 数字类型转换为 DOUBLE;
  • 时间类型(TIMESTAMP、DATE、TIME)转化为 Excel 序列号数字,并且赋予对应的显示格式;
  • TIMESTAMP_TZ 以及 TIME_TZ 转换为 UTC 时间并且丢失时区信息;
  • BOOLEAN 类型转化为 1 或者 0,并且设置显示为 TRUE 或者 FALSE 的特殊格式;
  • 其他类型转化为文本格式。

最后,我们看一个结合 Excel 导入与导出的示例。假设每个月的销售数据分别存储在一个 Excel 文件中,名称为 sales_202601.xlsx、sales_202602.xlsx、sales_202603.xlsx 等。

以下语句可以完成联合读取多个文件、执行数据分析、写入结果文件的多个操作:

sql 复制代码
COPY ( -- 导出结果文件
    WITH sales AS ( -- 联合读取文件
        SELECT * FROM 'sales_202601.xlsx'
        UNION ALL
        SELECT * FROM 'sales_202602.xlsx'
        UNION ALL
        SELECT * FROM 'sales_202603.xlsx'
    )
    SELECT region, SUM(amount) -- 执行数据分析
    FROM sales
    GROUP BY region
)
TO 'region_sales.xlsx' (FORMAT xlsx, HEADER true);

其中,WITH 相当于定义了一个临时表 sales,包含了多个月份的销售数据;然后基于 sales 进行数据分析;COPY 语句用于导出结果。

相关推荐
SelectDB24 分钟前
时间旅行与快照能力速查:Doris 侧三条落地路径
大数据·数据库·数据分析
SelectDB33 分钟前
云数仓成本结构拆解速查:五层计费 vs Doris / 托管形态
大数据·数据库·数据分析
SelectDB33 分钟前
什么场景能承接与建议保留:Doris vs Snowflake 边界速查
大数据·数据库·数据分析
注塑机数据采集联网7 小时前
如何解决注塑机数据采集联网对接MES系统数据WiFi传输数据不稳问题
大数据·数据库·物联网·数据挖掘·数据分析
benchmark_cc8 小时前
实时行情和分钟 K 线应该如何配合使用?从信号计算到交易决策的完整分析
大数据·python·数据分析·pandas·量化交易·股票数据·quantdash
roman_日积跬步-终至千里8 小时前
【数据Agent论文·分析与报告评测|27】ComInsight:原子洞察正确,组合后的结论就可靠吗?
人工智能·算法·机器学习·数据分析
学术 学术 Fun9 小时前
科研公开数据集怎么找?从关键词到许可证与数据引用
数据分析·科研·开放数据
roman_日积跬步-终至千里10 小时前
【数据Agent论文·企业系统与可靠性|29】QwenPaw-Data:企业分析Agent为什么要分别管理事实、方法和执行?
人工智能·数据分析
遇码19 小时前
流式调用中途断连别直接报错:瞬态故障识别、退避重试与中文根因诊断的搭建方法
大数据·人工智能·ai·rust·数据分析·大模型·agent