Featured image of post 用 DuckDB 搭建电商周报自动化系统——从 CSV 到可卖的数据产品

用 DuckDB 搭建电商周报自动化系统——从 CSV 到可卖的数据产品

手把手教你用 DuckDB 搭建电商周报自动化系统:从 CSV 直读到周报生成,含完整 Python 代码、环比分析、留存计算。学会这套方法论,可卖给中小企业或做成订阅产品。

引言:你的时间值多少钱?

你是不是也经历过这种场景——每周一早上,打开 Excel,手动复制上周末的销售数据,拼凑一张报表,然后发给老板。老板看后说"加几个维度",你又得重来。

如果这套流程用 DuckDB 自动化,同样的工作从一天缩短到 10 分钟,而且每周自动生成、永远不出错。更重要的是——这套系统是可以卖钱的。

今天我来拆解一个真实的付费项目:用 DuckDB + Python 搭建一套自动化电商周报系统。这套方法论我已经用在了三个电商客户身上,每个客户收费 3000-5000 元/月。

一、项目架构:为什么选 DuckDB?

传统方案:Python 脚本 → MySQL → Airflow → Jupyter → 邮件发送。开发周期长、维护成本高、客户没 DBA。

DuckDB 方案:Python + DuckDB,一个脚本搞定,零运维。

DuckDB 的四大优势

  1. 分析性能接近列式数据库:列式存储架构,聚合计算比 pandas 快 10 倍以上
  2. 原地读取 CSV/JSON/Parquet:无需 ETL,直接 read_csv_auto() 加载
  3. Python 无缝集成:duckdb 库直接执行 SQL,结果转 pandas
  4. 单文件数据库:复制即用,部署成本趋近于零

二、搭建数据管道:从 CSV 到 DuckDB

假设电商平台导出的 CSV 包含订单、商品、用户三张表。

import duckdb
import pandas as pd
from pathlib import Path
from datetime import datetime, timedelta

# 连接到 DuckDB 数据库(持久化存储)
db_path = Path("ecommerce.duckdb")
con = duckdb.connect(str(db_path))

# 原地读取 CSV(无需先加载到内存)
con.execute("""
    CREATE TABLE IF NOT EXISTS orders AS 
    SELECT * FROM read_csv_auto('orders.csv')
""")
con.execute("""
    CREATE TABLE IF NOT EXISTS products AS 
    SELECT * FROM read_csv_auto('products.csv')
""")
con.execute("""
    CREATE TABLE IF NOT EXISTS users AS 
    SELECT * FROM read_csv_auto('users.csv')
""")

# 查看表结构
print(con.execute("DESCRIBE orders").fetchdf())
print(con.execute("SELECT COUNT(*) FROM orders").fetchone())

关键点

  • read_csv_auto() 自动推断列类型,无需手动指定
  • CREATE TABLE IF NOT EXISTS 保证幂等性,重复运行不报错
  • DuckDB 直接读取 CSV,无需先导入数据库

三、核心分析逻辑:周报指标设计

周报的核心是指标树设计,让非技术客户也能看懂。

# 创建周度销售视图(按周自动切片)
con.execute("""
CREATE VIEW IF NOT EXISTS v_weekly_sales AS
SELECT 
    DATE_TRUNC('week', order_date) AS week,
    COUNT(*) AS total_orders,
    SUM(amount) AS total_revenue,
    AVG(amount) AS avg_order_value,
    COUNT(DISTINCT user_id) AS active_users,
    COUNT(DISTINCT product_id) AS unique_products
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '12 weeks'
GROUP BY DATE_TRUNC('week', order_date)
ORDER BY week DESC
""")

# 验证视图
print(con.execute("SELECT * FROM v_weekly_sales LIMIT 5").fetchdf())

指标设计原则

  • 核心指标放前面:订单数、营收、客单价、活跃用户
  • 增加环比:让客户一眼看出趋势
  • 数据保留最近 12 周:够用,不浪费存储

四、生成周报:Python 完整代码

def generate_weekly_report(db_path: str) -> dict:
    """生成周度经营报告"""
    con = duckdb.connect(db_path)
    
    # 获取本周和上周数据
    current_week = con.execute("""
        SELECT * FROM v_weekly_sales 
        WHERE week = DATE_TRUNC('week', CURRENT_DATE)
    """).fetchone()
    
    prev_week = con.execute("""
        SELECT * FROM v_weekly_sales 
        WHERE week = (
            SELECT MAX(week) FROM v_weekly_sales 
            WHERE week < DATE_TRUNC('week', CURRENT_DATE)
        )
    """).fetchone()
    
    # 环比计算函数
    def pct_change(curr, prev):
        if prev is None or prev == 0: 
            return None
        return round((curr - prev) / prev * 100, 2)
    
    report = {
        "week": current_week[0].strftime('%Y-%m-%d') if current_week else None,
        "total_orders": current_week[1] if current_week else 0,
        "total_revenue": round(current_week[2], 2) if current_week else 0,
        "revenue_growth": pct_change(current_week[2], prev_week[2]) if current_week and prev_week else None,
        "avg_order_value": round(current_week[3], 2) if current_week else 0,
        "avg_order_growth": pct_change(current_week[3], prev_week[3]) if current_week and prev_week else None,
        "active_users": current_week[4] if current_week else 0,
        "user_growth": pct_change(current_week[4], prev_week[4]) if current_week and prev_week else None,
    }
    
    # 热销品类 TOP5
    report["top_categories"] = con.execute("""
        SELECT p.category, SUM(o.amount) as revenue
        FROM orders o 
        JOIN products p ON o.product_id = p.id
        WHERE DATE_TRUNC('week', o.order_date) = DATE_TRUNC('week', CURRENT_DATE)
        GROUP BY p.category
        ORDER BY revenue DESC
        LIMIT 5
    """).fetchall()
    
    # 用户留存分析(窗口函数)
    report["retention"] = con.execute("""
        SELECT 
            first_week,
            COUNT(DISTINCT user_id) as new_users,
            SUM(CASE WHEN weeks_active >= 2 THEN 1 ELSE 0 END) as retained_2w,
            SUM(CASE WHEN weeks_active >= 3 THEN 1 ELSE 0 END) as retained_3w
        FROM (
            SELECT 
                user_id,
                DATE_TRUNC('week', MIN(order_date)) as first_week,
                COUNT(DISTINCT DATE_TRUNC('week', order_date)) as weeks_active
            FROM orders
            WHERE order_date >= CURRENT_DATE - INTERVAL '12 weeks'
            GROUP BY user_id
        )
        GROUP BY first_week
        ORDER BY first_week DESC
        LIMIT 4
    """).fetchall()
    
    con.close()
    return report

五、导出报告:多种格式适应不同场景

import json
from pathlib import Path

def export_report(report: dict, output_dir: str):
    """导出为多种格式"""
    out = Path(output_dir)
    out.mkdir(parents=True, exist_ok=True)
    
    # JSON 格式(供 API 调用)
    (out / "report.json").write_text(
        json.dumps(report, ensure_ascii=False, indent=2)
    )
    
    # Markdown 格式(可直接发微信/飞书)
    md = f"""# 周度经营报告 {report['week']}

## 核心指标
- **订单数**: {report['total_orders']:,}{'↑' if report['revenue_growth'] and report['revenue_growth'] > 0 else '↓'} {abs(report['revenue_growth']):.1f}%)
- **营收**: ¥{report['total_revenue']:,.2f}{'↑' if report['revenue_growth'] and report['revenue_growth'] > 0 else '↓'} {abs(report['revenue_growth']):.1f}%)
- **客单价**: ¥{report['avg_order_value']:.2f}{'↑' if report['avg_order_growth'] and report['avg_order_growth'] > 0 else '↓'} {abs(report['avg_order_growth']):.1f}%)
- **活跃用户**: {report['active_users']:,}

## 热销品类 TOP5
"""
    for i, (cat, rev) in enumerate(report['top_categories'], 1):
        md += f"{i}. {cat}: ¥{rev:,.2f}\n"
    
    (out / "report.md").write_text(md)
    print(f"✅ 报告已生成: {out}")

# 使用示例
if __name__ == "__main__":
    report = generate_weekly_report("ecommerce.duckdb")
    export_report(report, "reports/2026-W31")

六、完整工作流:一键生成周报

# main.py
from pathlib import Path
import duckdb
from datetime import datetime

def main():
    # 1. 加载数据(幂等)
    con = duckdb.connect("ecommerce.duckdb")
    
    # 自动增量更新
    csv_files = list(Path("data").glob("orders_*.csv"))
    for f in sorted(csv_files)[-1:]:  # 只加载最新文件
        con.execute(f"INSERT INTO orders SELECT * FROM read_csv_auto('{f}')")
    
    # 2. 生成报告
    report = generate_weekly_report("ecommerce.duckdb")
    
    # 3. 导出
    week_str = report['week'].replace('-', '')
    export_report(report, f"reports/{week_str}")
    
    con.close()
    print("🎉 周报生成完成")

if __name__ == "__main__":
    main()

七、变现路径:这个产品值多少钱?

定价策略

模式价格适用场景
一次性定制5000-15000 元中小电商企业
月度订阅2000-5000 元/月品牌方、代运营
模板化 SaaS99-299 元/月多个客户复用

关键护城河

  1. 指标体系设计:什么指标客户真的关心(不是技术,是业务理解)
  2. 异常检测逻辑:发现问题比展示数据值钱
  3. 交付体验:格式、频率、可解释性

进阶方向

  • 接入实时数据源(Kafka + DuckDB Streaming)
  • 添加预测模块(Prophet / LightGBM)
  • 搭建 Web 界面(Streamlit / Gradio)
  • 多租户隔离(每个客户一个 DuckDB 文件)

八、与传统方案对比

维度传统方案(MySQL + Airflow)DuckDB 方案
开发周期2-4 周1-2 天
运维成本需要 DBA零运维
部署成本服务器 + 数据库单机即可
查询性能需要建索引列式存储,开箱即用
数据更新ETL 流程复杂CSV 直读,即插即用
可移植性绑定特定数据库单文件,复制即用

九、实战案例:我卖给客户的真实报价

上个月帮一家电商客户搭建了这个系统:

  • 数据源:Shopify 导出 CSV,约 30 万行/月
  • 指标:订单数、营收、客单价、活跃用户、留存率
  • 交付物:每周自动发送的 Markdown 报告
  • 收费:一次性 8000 元 + 月度维护 2000 元/月

客户反馈:“比之前找外包做的快多了,而且改起来也方便。”

十、总结

用 DuckDB 搭建电商周报自动化系统,核心就三步:

  1. 读数据read_csv_auto() 一行代码搞定
  2. 算指标:窗口函数 + 视图,SQL 即分析
  3. 导出报告:Python 格式化,Markdown/JSON 双格式

这套方法论可以复制到:

  • 财务月报系统
  • 广告投放 ROI 追踪
  • SaaS 订阅分析
  • 库存预警系统

记住:数据产品的价值不在技术,而在业务理解。同样的代码,换个行业就是另一个产品。


📖 本文的完整教程(包含数据源模拟、异常检测、Streamlit 部署)已发布在 duckdblab.org,建议收藏系统学习。

💡 想系统学习 DuckDB 在电商场景的更多应用?duckdblab.org 上有完整的实战教程系列,从入门到变现全覆盖。

📺 Watch video tutorials → Olap Studio YouTube

Subscribe for more DuckDB & AI automation tutorials

使用 Hugo 构建
主题 StackJimmy 设计

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

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

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