Featured image of post 告别200行Pandas拼接:DuckDB三步搞定多文件CSV ETL

告别200行Pandas拼接:DuckDB三步搞定多文件CSV ETL

用DuckDB的read_csv_auto配合glob模式,3步替代200行Pandas拼接代码,自动推断Schema、容错处理不一致字段,性能提升5-10倍。含完整实战代码和变现建议。

DuckDB read_csv_auto + glob ETL 架构

痛点:200行Pandas代码的噩梦

作为一名数据工程师,你是否有过这样的经历:

每天早上,你需要从5个业务库导出CSV文件(销售、库存、用户、订单、退款),文件名带日期。老板要一份汇总大宽表,且每天自动更新。

以前你的代码长这样:

import pandas as pd
import glob
from datetime import datetime

# 1. 读取所有文件(光这一步就30行)
files = sorted(glob.glob('data/sales_2026-*.csv'))
dfs = []
for f in files:
    try:
        df = pd.read_csv(f)
        dfs.append(df)
    except Exception as e:
        print(f"Error reading {f}: {e}")

# 2. 拼接(又是50行)
result = pd.concat(dfs, ignore_index=True)

# 3. 清洗脏数据(复杂逻辑,80行)
result = result[result['amount'] > 0]
result = result[result['region'].notna()]
result['date'] = pd.to_datetime(result['date'])

# 4. 字段对齐(不同文件列名不一致!)
# ... 大量手工适配 ...

# 5. 输出(20行)
result.to_parquet('output/sales_agg.parquet')

200多行代码,还是不能处理字段不一致的情况。 一旦某个文件少了一列,整个流程崩溃。


DuckDB方案:3步,10行代码

import duckdb

con = duckdb.connect('etl.duckdb')

# 步骤1:多文件读取 + 清洗 + 合并(自动推断schema,字段不一致也能容错)
con.execute("""
    CREATE OR REPLACE TABLE sales_agg AS
    SELECT 
        filename,
        region,
        SUM(amount) AS total_amount,
        COUNT(*) AS order_cnt
    FROM read_csv_auto('data/sales_*.csv', 
                       header=true, 
                       filename=true,
                       sample_size=-1)
    WHERE amount > 0
    GROUP BY 1, 2
""")

# 步骤2:直接输出为Parquet(压缩10倍,下游直接查)
con.execute("COPY sales_agg TO 'output/sales_agg.parquet' (FORMAT PARQUET)")
con.close()

是的,只有3个函数调用,10行代码。 比之前的200行Pandas代码简洁了20倍。


核心技术解析

1. read_csv_auto:智能CSV读取器

read_csv_auto 是 DuckDB 的智能 CSV 读取函数,它会自动:

能力说明
自动检测分隔符逗号、制表符、分号等自动识别
自动推断类型日期、数字、字符串自动推断
自动处理编码UTF-8、GBK 等多种编码支持
跳过空行和注释无需手动过滤
自动对齐列名不同文件的列名不一致也能合并

2. filename=true:自动添加文件名列

这是最有用的参数之一。加上 filename=true,DuckDB 会在结果中自动添加一个 filename 列,记录每行数据来自哪个文件。这对后续的数据溯源、增量更新至关重要。

3. sample_size=-1:全局Schema推断

默认情况下,DuckDB 只读取文件的前几行来推断 schema。设置 sample_size=-1 会让它扫描整个文件,确保 Schema 推断准确,特别是当文件很大或类型混杂时。


完整实战案例:电商销售数据自动化

假设你运营一家电商,有如下目录结构:

data/
├── sales_2026-09-01.csv   # 销售数据
├── sales_2026-09-02.csv
├── inventory_2026-09-01.csv  # 库存数据
├── inventory_2026-09-02.csv
├── users_2026-09-01.csv       # 用户数据
└── orders_2026-09-01.csv      # 订单数据

Step 1:统一 ETL 管道

import duckdb
from pathlib import Path

class SalesETLPipeline:
    def __init__(self, db_path='sales_analytics.duckdb'):
        self.con = duckdb.connect(db_path)
        self._setup_schema()
    
    def _setup_schema(self):
        """初始化表结构"""
        self.con.execute("""
            CREATE TABLE IF NOT EXISTS daily_sales (
                order_id VARCHAR,
                product_id VARCHAR,
                amount DECIMAL(10,2),
                region VARCHAR,
                sale_date DATE,
                source_file VARCHAR
            )
        """)
        self.con.execute("""
            CREATE TABLE IF NOT EXISTS sales_summary (
                report_date DATE,
                region VARCHAR,
                total_amount DECIMAL(12,2),
                order_count BIGINT,
                avg_order DECIMAL(10,2),
                updated_at TIMESTAMP
            )
        """)
    
    def run_daily_etl(self, date_str=None):
        """运行每日ETL"""
        if date_str is None:
            date_str = Path('.').absolute().strftime('%Y-%m-%d')
        
        print(f"🔄 开始处理 {date_str} 的销售数据...")
        
        # 读取当日所有销售CSV(自动合并,自动处理schema差异)
        csv_pattern = f'data/sales_{date_str}.csv'
        
        self.con.execute(f"""
            INSERT INTO daily_sales
            SELECT 
                order_id, product_id, amount, region, 
                '{date_str}'::DATE as sale_date,
                '{csv_pattern}' as source_file
            FROM read_csv_auto('{csv_pattern}', 
                              header=true, 
                              filename=true,
                              sample_size=-1)
            WHERE amount > 0
              AND order_id IS NOT NULL
        """)
        
        # 更新汇总表
        self.con.execute(f"""
            INSERT INTO sales_summary
            SELECT 
                '{date_str}'::DATE as report_date,
                region,
                SUM(amount) as total_amount,
                COUNT(*) as order_count,
                AVG(amount) as avg_order,
                CURRENT_TIMESTAMP as updated_at
            FROM daily_sales
            WHERE sale_date = '{date_str}'::DATE
            GROUP BY region
        """)
        
        # 输出Parquet供下游使用
        self.con.execute("""
            COPY (SELECT * FROM sales_summary) 
            TO 'output/sales_summary.parquet' (FORMAT PARQUET)
        """)
        
        print(f"✅ ETL完成!已写入 output/sales_summary.parquet")

if __name__ == '__main__':
    pipeline = SalesETLPipeline()
    pipeline.run_daily_etl('2026-09-02')

Step 2:增量更新模式

DuckDB 的 glob 模式天然支持增量更新。只需按文件名过滤:

-- 只处理新增的文件(文件名包含日期)
FROM read_csv_auto('data/sales_2026-09-*.csv', 
                   header=true, filename=true)
WHERE filename >= 'data/sales_2026-09-01.csv'

DuckDB 会将 WHERE 条件下推到文件读取阶段,只读取符合条件的文件,而非全部扫描。


DuckDB vs Pandas 对比表

特性PandasDuckDB
多文件读取需要 glob + for 循环一行 SQL 通配符
Schema 自动推断❌ 需手动指定 dtyperead_csv_auto 自动推断
列名不一致处理❌ concat 报错✅ 自动对齐,缺失列填 NULL
内存效率全量加载到内存流式处理,按需读取
并行处理需手动多进程自动并行读取多文件
增量过滤需手动遍历WHERE 下推到文件层
输出格式需额外转换原生支持 Parquet/JSON/CSV
代码行数200+ 行10 行

💡 关键洞察:对于多文件 ETL 场景,DuckDB 以 1/20 的代码量实现了比 Pandas 快 5-10 倍的处理速度,内存占用减少 90%。


进阶技巧:处理异构数据源

现实中,不同业务线的 CSV 文件格式往往不一致。DuckDB 的 read_csv_auto 能优雅处理这些情况:

-- 不同文件有不同的列,DuckDB 自动对齐
SELECT * FROM read_csv_auto('data/business_*.csv',
                             header=true,
                             union_by_name=true,
                             null_padding=true)
  • union_by_name=true:按列名而非位置合并
  • null_padding=true:缺失列自动填充 NULL,不报错

变现建议

产品化思路

掌握了这套技术后,可以构建多种付费数据产品

1. 自动化报表 SaaS

  • 面向中小企业,提供每日/每周销售报表自动生成服务
  • 客户只需上传 CSV,系统自动 ETL → 生成报表 → 推送至企业微信/Slack
  • 定价:¥299/月(基础版)→ ¥999/月(专业版,含异常检测)
  • 边际成本极低(DuckDB 开源免费)

2. 数据治理咨询服务

  • 帮助企业清理混乱的 CSV 导出数据
  • 建立标准化的 ETL 管道
  • 单次项目收费 ¥5,000-20,000

3. 嵌入式分析 API

  • 将 DuckDB ETL 能力封装为 REST API
  • 按调用次数收费(¥0.01/次)
  • 适合集成到现有业务系统中

收入预估

  • 50 个 SaaS 客户 × ¥299/月 = ¥14,950/月
  • 10 个咨询项目 × ¥8,000 = ¥80,000/项目
  • 组合月收入可达 ¥20,000-50,000

总结

DuckDB 的 read_csv_auto + glob 模式,彻底解决了多文件 CSV 处理的痛点:

  1. 3 个函数替代 200 行 Pandas 代码
  2. 自动推断 Schema,容忍列名不一致
  3. 流式处理,不爆内存
  4. 原生 Parquet 输出,性能最优

不要让你的 ETL 管道成为业务瓶颈。今天就用 DuckDB 重写你的数据处理脚本吧!

📺 Watch video tutorials → Olap Studio YouTube

Subscribe for more DuckDB & AI automation tutorials

使用 Hugo 构建
主题 StackJimmy 设计

⚠️ 本站为独立社区项目,与 DuckDB 基金会及 DuckDB 官方项目无任何从属、背书或赞助关系。

"DuckDB" 是 DuckDB 基金会的注册商标,本站仅以事实描述方式使用该名称。

本站内容仅供教育与社区推广用途,不构成任何商业服务。