DuckDB 电商日报实战:5 条 SQL 搞定自动化销售报表
如果你靠数据分析接外包,每天最头疼的事不是写 SQL,而是每天重复跑同一套报表:给客户的老板看昨日销售汇总、品类排行、城市热度、复购趋势……这套流程做一单收费 500-2000 块,但每次都要花 1-2 小时手工处理。
今天拆解一个真实可复现的项目:用 DuckDB 构建一套全自动电商日报系统,从数据采集到可视化报表一键生成。做完之后,这个系统可以卖给任何电商卖家,按 SaaS 模式每月收 99 元/月。

一、项目架构:3 个文件搞定整套系统
整个系统只需 3 个文件:
generate_report.py—— 核心报告生成脚本daily_report.md.j2—— Jinja2 模板config.yaml—— 配置文件
不需要数据库,不需要 Web 框架。 DuckDB 直接读 CSV/Parquet,用 Jinja2 生成 Markdown,用 SMTP 发邮件。
系统数据流如下:
订单 CSV → DuckDB (直接读取) → 5 条 SQL 查询 → Jinja2 模板 → Markdown 日报 → 邮件发送
完整 SQL 查询只需 5 条核心语句,下面逐条拆解。
二、核心查询 1:实时销售总览
第一步,用一条查询算出当天的关键 KPI。
SELECT
COUNT(*) AS total_orders,
ROUND(SUM(amount), 2) AS total_revenue,
ROUND(AVG(amount), 2) AS avg_order_value,
COUNT(DISTINCT customer) AS unique_customers,
MAX(order_date) AS latest_order_date
FROM sales
WHERE order_date = CURRENT_DATE;
这条查询的返回值就是你每天报表最开头的 KPI 卡片:
📊 昨日销售总览 (2026-06-09)
━━━━━━━━━━━━━━━━━━━━━━━━━━
📦 订单总数:42 单
💰 总销售额:¥186,420.00
🎯 平均客单价:¥4,438.57
👥 独立客户:38 人
变现要点:很多电商卖家用的是千牛/生意参谋自带的看板,但这些工具看不到「独立客户数」「客单价趋势」这类自定义指标。你的日报多这 2-3 个维度,就是客户愿意付费的理由。
与传统工具对比
| 工具 | 实现时间 | 代码量 | 部署难度 | 适合场景 |
|---|---|---|---|---|
| Excel/VBA | 2-3 天 | 50+ 行 | 中 | 小型店铺,手动更新 |
| Pandas + Jupyter | 1 天 | 80+ 行 | 中 | 数据分析师自用 |
| Power BI | 3-5 天 | 可视化拖拽 | 高 | 需要交互式看板 |
| DuckDB + Python | 2 小时 | 30 行 | 低 | 自动化日报,零运维 |
三、核心查询 2:品类深度分析
第二步,分析品类的收入贡献和潜力。
WITH category_stats AS (
SELECT
category,
COUNT(*) AS orders,
ROUND(SUM(amount), 2) AS revenue,
ROUND(AVG(amount), 2) AS avg_price,
COUNT(DISTINCT customer) AS customers,
ROUND(
SUM(amount) * 100.0 / SUM(SUM(amount)) OVER(), 1
) AS revenue_share
FROM sales
WHERE order_date = CURRENT_DATE
GROUP BY category
),
ranked AS (
SELECT *,
ROW_NUMBER() OVER (ORDER BY revenue DESC) AS rev_rank,
ROW_NUMBER() OVER (ORDER BY customers DESC) AS cust_rank
FROM category_stats
)
SELECT
category,
orders,
revenue,
avg_price,
customers,
revenue_share,
rev_rank,
cust_rank
FROM ranked
ORDER BY revenue DESC;
输出示例:
品类收入排行 (2026-06-09)
━━━━━━━━━━━━━━━━━━━━━━━━━━
① 电子产品 — ¥86,390 (占比 46.3%) — 客单价 ¥8,639
② 家电 — ¥35,994 (占比 19.3%) — 客单价 ¥5,999
③ 外设 — ¥10,096 (占比 5.4%) — 客单价 ¥2,524
④ 个护 — ¥4,598 (占比 2.5%) — 客单价 ¥2,299
变现要点:这里用到了 DuckDB 的窗口函数 SUM() OVER() 做实时占比计算。这个能力在 MySQL 里也能做但效率差很多,在 Pandas 里需要写 10 行代码。强调这个效率差异,是说服客户从 Excel/Pandas 迁移到 DuckDB 的关键话术。
这里的关键技术点是 窗口函数(Window Functions)。SUM() OVER() 让你在不写子查询的情况下计算聚合占比,ROW_NUMBER() 帮你做排名。这些都是 DuckDB 的内置能力,无需额外安装。
四、核心查询 3:城市热力图
第三步,找出高价值城市。
SELECT
city,
COUNT(*) AS orders,
ROUND(SUM(amount), 2) AS total_revenue,
ROUND(AVG(amount), 2) AS avg_order,
COUNT(DISTINCT customer) AS unique_customers,
ROUND(PERCENT_RANK() OVER (ORDER BY total_revenue), 3) AS revenue_percentile,
ROUND(PERCENT_RANK() OVER (ORDER BY avg_order), 3) AS avg_order_percentile
FROM sales
WHERE order_date = CURRENT_DATE
GROUP BY city
ORDER BY total_revenue DESC
LIMIT 15;
输出示例:
城市价值排行 Top 10
━━━━━━━━━━━━━━━━━━━━━━━━━━
北京 — ¥19,999 (收入百分位: 1.000) 🏆
长沙 — ¥18,999 (收入百分位: 0.950)
上海 — ¥15,999 (收入百分位: 0.900)
大连 — ¥14,999 (收入百分位: 0.850)
宁波 — ¥12,999 (收入百分位: 0.800)
变现要点:PERCENT_RANK() 这个函数是 DuckDB 的杀手锏。它帮你快速看出哪个城市虽然订单量不大,但客单价极高——这样的城市值得投放定向广告。很多电商运营需要手动在 Excel 里排序算排名,你一条 SQL 搞定。
DuckDB 的 PERCENT_RANK() vs 传统方法
在传统方案中,算百分位排名需要:
# Pandas 方案:需要多步操作
df['revenue_percentile'] = df['total_revenue'].rank(pct=True)
而 DuckDB 直接用 SQL 完成,一步到位,且可以在查询层面做优化:
-- DuckDB 一行搞定
PERCENT_RANK() OVER (ORDER BY total_revenue)
对于百万级数据,DuckDB 的处理速度通常比 Pandas 快 5-10 倍。
五、核心查询 4:复购率分析
第四步,算复购率——这是电商老板最关心的指标之一。
WITH customer_orders AS (
SELECT
customer,
COUNT(*) AS order_count,
MIN(order_date) AS first_order,
MAX(order_date) AS last_order,
ROUND(SUM(amount), 2) AS total_spent
FROM sales
GROUP BY customer
),
repurchase AS (
SELECT
COUNT(*) AS total_customers,
SUM(CASE WHEN order_count > 1 THEN 1 ELSE 0 END) AS repurchase_count
FROM customer_orders
)
SELECT
total_customers,
repurchase_count,
ROUND(repurchase_count * 100.0 / total_customers, 1) AS repurchase_rate,
ROUND(avg_clv, 2) AS avg_customer_lifetime_value
FROM repurchase
CROSS JOIN (
SELECT AVG(total_spent) AS avg_clv
FROM (
SELECT SUM(amount) AS total_spent
FROM sales
GROUP BY customer
) sub
) avg_calc;
输出示例:
🔄 复购分析
━━━━━━━━━━━━━━━━━━━━━━━━━━
👥 总客户数:128 人
🔁 复购客户:23 人 (18.0%)
💎 客户终身价值:¥8,456.32
变现要点:复购率是电商的生命线。18% 的复购率对于一般电商算合格,但如果能做到 30%+,客户就能大幅降低获客成本。这个分析直接关联到 ROI 计算——每提升 1% 的复购率,意味着每月可以多赚多少。
这里用到了 CTE(公用表表达式) 的分步写法。先按客户聚合订单,再统计复购人数,最后计算比率。CTE 让 SQL 逻辑像写代码一样层次分明,比嵌套子查询可读性好太多。
六、核心查询 5:趋势对比(与昨日比)
第五步,做同比——这是日报最有价值的部分,因为它回答了「今天比昨天好还是差」。
WITH today AS (
SELECT
COUNT(*) AS orders,
ROUND(SUM(amount), 2) AS revenue,
ROUND(AVG(amount), 2) AS avg_order
FROM sales
WHERE order_date = CURRENT_DATE
),
yesterday AS (
SELECT
COUNT(*) AS orders,
ROUND(SUM(amount), 2) AS revenue,
ROUND(AVG(amount), 2) AS avg_order
FROM sales
WHERE order_date = CURRENT_DATE - INTERVAL 1 DAY
)
SELECT
t.orders, t.revenue, t.avg_order,
y.orders AS prev_orders,
y.revenue AS prev_revenue,
y.avg_order AS prev_avg_order,
ROUND((t.revenue - y.revenue) * 100.0 / NULLIF(y.revenue, 0), 1) AS revenue_change_pct,
ROUND((t.orders - y.orders) * 100.0 / NULLIF(y.orders, 0), 1) AS orders_change_pct
FROM today t
CROSS JOIN yesterday y;
输出示例:
📈 环比昨日变化
━━━━━━━━━━━━━━━━━━━━━━━━━━
📦 订单量:42 (+12.8%)
💰 销售额:¥186,420 (+8.3%)
🎯 客单价:¥4,438.57 (-3.9%)
变现要点:NULLIF() 是 DuckDB 处理除零错误的最佳实践。这条查询看起来简单,但包含了时间序列分析的核心思路——用 CTE 实现自关联对比,不需要任何外部工具。
NULLIF() 的重要性
在 SQL 中,除零是一个经典的陷阱。NULLIF(a, 0) 当 a 为 0 时返回 NULL,这样整个除法的结果就是 NULL 而不是报错。配合 ROUND() 函数,可以让报表在数据为空时依然干净整洁。
七、自动化部署:cron + Python 一键运行
用 cron 每天凌晨 2 点自动生成报告并发送邮件:
# 编辑 crontab
crontab -e
# 添加以下行:每天凌晨 2 点生成并发送日报
0 2 * * * cd /root/telegram-duckdb-bot && python3 generate_report.py --send-email
generate_report.py 的核心逻辑:
import duckdb
import jinja2
import smtplib
from email.mime.text import MIMEText
from datetime import date
# 1. 连接 DuckDB(直接读 Parquet/CSV)
con = duckdb.connect("ecommerce.duckdb")
# 2. 执行所有查询,获取结果
kpi_result = con.execute("""
SELECT COUNT(*) AS total_orders,
ROUND(SUM(amount), 2) AS total_revenue,
ROUND(AVG(amount), 2) AS avg_order_value,
COUNT(DISTINCT customer) AS unique_customers
FROM sales
WHERE order_date = CURRENT_DATE
""").fetchdf()
# 3. 用 Jinja2 模板渲染 Markdown 报告
template = jinja2.Template(open("daily_report.md.j2").read())
report_md = template.render(
date=date.today(),
kpi=kpi_result.to_dict('records')[0],
)
# 4. 发送邮件
msg = MIMEText(report_md, 'html', 'utf-8')
msg['Subject'] = f'电商日报 {date.today()}'
msg['From'] = '[email protected]'
msg['To'] = '[email protected]'
smtp = smtplib.SMTP('smtp.yourdomain.com', 587)
smtp.starttls()
smtp.login('user', 'password')
smtp.send_message(msg)
整个系统从数据采集到邮件发送,核心代码不超过 50 行。DuckDB 处理 100 万行数据通常只需要 0.5-2 秒,远快于 Pandas 的 3-8 秒。
八、与传统方案的完整对比
下面从多个维度对比这套 DuckDB 方案与传统电商数据分析方案:
| 维度 | Excel/手工 | Pandas + Jupyter | Power BI | DuckDB 日报系统 |
|---|---|---|---|---|
| 搭建时间 | 数天 | 半天 | 1-2 周 | 2 小时 |
| 维护成本 | 高(每次手动) | 中 | 高 | 低(cron 自动) |
| 查询速度 | 慢(>10万行卡顿) | 中 | 中 | 快(列式存储) |
| 扩展性 | 差 | 中 | 差 | 好(支持 Parquet) |
| 月成本 | ¥0 + 人力 | ¥0 + 人力 | ¥500+/月 | ¥0 + 服务器 ¥50 |
| 适合数据量 | <10 万行 | <1000 万行 | <1 亿行 | 任意(磁盘处理) |
九、商业化路径:三种变现方式
这个项目做完后的变现路径非常清晰:
路径 A:卖模板(一次收费 ¥199)
把上面的脚本包装成一个「电商日报自动化模板」,在知识星球/V2EX/掘金等平台分享部分 SQL,完整版模板收费 199 元。
目标用户:电商运营、数据分析师、小型工作室
路径 B:SaaS 服务(持续收入 ¥99/月/店)
把脚本部署到云服务器,做成 Web 产品。客户只需把 CSV 订单上传,每天自动收到 Markdown/PDF 日报。
定价:99 元/月/店,支持多店 目标用户:中小电商卖家(他们请不起专职数据分析师)
路径 C:外包交付(高客单 ¥500-2000/单)
帮客户定制化配置:接入他们的订单系统(如 Shopify、有赞、千牛),定制报表模板,设置自动化邮件。
交付周期:1-2 天 利润率:80%+(主要是 SQL 和 Python 脚本,几乎零边际成本)
路径 D:数据监控即服务(进阶)
在日报基础上增加异常检测:当销售额突然下降超过 20% 时,自动发 Telegram 消息给老板。这个增值功能可以单独收费 ¥49/月。
十、进阶扩展方向
当客户用上基础日报后,可以继续扩展:
- 周报/月报:在日报基础上聚合周/月数据,增加同比环比趋势图
- 竞品分析:用
httpfs扩展爬取竞品网站价格数据,用 DuckDB 对比 - 客户画像:基于购买记录,用 DuckDB 的
UNNEST和列表函数分析客户偏好 - 可视化升级:把 Markdown 报表对接到 Evidence 或 Grafana,生成交互式看板
- 多店铺聚合:用 DuckDB 的
read_parquet+ glob 模式,同时处理多个店铺的 Parquet 文件
总结
这套 DuckDB 电商日报系统的核心优势在于:
- 5 条 SQL 覆盖 90% 的日报需求:销售总览、品类分析、城市热力、复购率、趋势对比
- 零依赖部署:不需要数据库、Web 框架或 BI 工具
- 自动化运行:cron + Python 每天自动执行,老板每天醒来就能看到报表
- 可规模化:从 1 家店扩展到 100 家店,代码几乎不用改
对于数据服务从业者来说,这就是一个标准的「一次开发,反复售卖」的赚钱项目。掌握 DuckDB 在这类场景中的用法,能让你在接外包和做 SaaS 时比别人快 10 倍。
📖 本文的完整版已发布在 duckdblab.org,包含更详细的代码文件、Jinja2 模板和可下载的示例数据集。
💡 想系统学习 DuckDB 在电商分析中的应用?duckdblab.org 上有从入门到变现的完整教程系列,涵盖 Parquet 处理、窗口函数实战、自动化部署等进阶主题。