Featured image of post DuckDB 零 ETL 日报系统:5 分钟从 CSV 到 Telegram 推送

DuckDB 零 ETL 日报系统:5 分钟从 CSV 到 Telegram 推送

用 DuckDB 直接读取 CSV 文件,无需导入数据库,配合 Python + Telegram Bot 实现全自动日报系统。零 ETL、零运维,5 分钟搭建可变现的数据产品。

DuckDB 零 ETL 日报系统:5 分钟从 CSV 到 Telegram 推送

每天早上醒来,手机收到一份格式精美的日报——这不是幻想,而是用 DuckDB + Python + Telegram Bot 搭建的系统能做到的事。

传统报表系统的痛点很清晰:需要 ETL 管道、数据库服务器、调度工具(Airflow 或 cron),维护成本高昂。DuckDB 的颠覆性在于:它让报表系统回归极简——一个 CSV 文件,十行代码,就能完成从原始数据到推送通知的全流程。

今天拆解一个可直接落地的副业项目:用 DuckDB 搭建零 ETL 自动化日报系统,帮助中小企业每天自动产出核心指标报告,推送到 Telegram 群或私聊。

DuckDB 零 ETL 日报系统架构


一、商业逻辑:你在卖什么?

很多人以为卖的是"数据报表",但真正的价值是**“省时间”**。

一个中小电商团队每天花费 30-60 分钟整理销售数据、制作报表。如果你能帮他们 automate 这个流程,每月收取 299 元订阅费,客户会非常乐意付费——因为他们买的是每天多出来的 1 小时。

定价策略参考:

  • 基础版(每日核心指标):299 元/月
  • 进阶版(含环比分析 + Top 商品排行):499 元/月
  • 企业版(多业务线 + 自定义指标):999 元/月

二、系统架构:三层极简设计

整个系统分三层,没有数据库服务器,没有 Airflow:

┌─────────────────────────────────────────────────────┐
│                   数据层 (Data Layer)                 │
│  CSV / Excel / JSON 文件 → DuckDB read_csv_auto()    │
│  零 ETL,直接原地读取                                 │
└────────────────────────┬────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────┐
│                   计算层 (Compute Layer)              │
│  Python + DuckDB SQL:聚合、环比、Top-N               │
│  百万行数据 1-2 秒出结果                              │
└────────────────────────┬────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────┐
│                   交付层 (Delivery Layer)              │
│  Telegram Bot API → 消息格式化 → 定时推送             │
│  支持:私聊 / 群组 / 频道                              │
└─────────────────────────────────────────────────────┘

核心优势: 一台年费 200 元的云服务器(或本地电脑)即可运行,无需任何第三方数据服务。


三、第一步:准备示例数据

先用 Python 生成一份模拟电商日报数据,验证系统逻辑:

import duckdb
import pandas as pd
from datetime import datetime, timedelta
import random

# 生成 30 天的模拟电商数据
dates = [datetime(2026, 8, 1) + timedelta(days=i) for i in range(30)]
products = ["笔记本", "鼠标", "键盘", "显示器", "耳机"]
categories = ["数码配件", "外设", "输入设备", "显示设备", "音频设备"]

data = []
for d in dates:
    for p, c in zip(products, categories):
        orders = random.randint(10, 200)
        revenue = orders * random.uniform(50, 500)
        users = random.randint(50, 500)
        data.append({
            "date": d.strftime("%Y-%m-%d"),
            "product": p,
            "category": c,
            "orders": orders,
            "revenue": round(revenue, 2),
            "users": users,
        })

df = pd.DataFrame(data)
df.to_csv("/tmp/ecommerce_daily.csv", index=False)
print(f"✅ 生成 {len(df)} 条记录")

运行后会在 /tmp/ecommerce_daily.csv 生成 150 条记录(30 天 × 5 个商品),每条包含日期、商品、品类、订单数、营收和活跃用户数。


四、第二步:DuckDB 核心查询(零 ETL)

DuckDB 的杀手锏是**read_csv_auto()**——直接读取 CSV 文件,自动推断列类型,无需任何导入操作:

import duckdb

con = duckdb.connect()

# 1. 核心指标汇总(昨日 vs 前日环比)
yesterday = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
day_before = (datetime.now() - timedelta(days=2)).strftime("%Y-%m-%d")

daily_report = con.execute("""
    SELECT 
        '昨日' as period,
        SUM(revenue) as total_revenue,
        SUM(orders) as total_orders,
        SUM(users) as total_users,
        ROUND(SUM(revenue)/NULLIF(SUM(orders),0), 2) as avg_order_value
    FROM read_csv_auto('/tmp/ecommerce_daily.csv')
    WHERE date = ?
""", [yesterday]).fetchdf()

print("📊 核心指标:")
print(daily_report.to_string(index=False))

关键点:

  • read_csv_auto() 自动识别日期格式、数字类型,无需手动指定 schema
  • 参数化查询(? 占位符)防止 SQL 注入
  • NULLIF() 防止除以零错误

五、第三步:环比计算与 Top-N 排行

日报的价值在于变化趋势,而不仅仅是绝对数值。用 DuckDB 的 CASE WHEN 实现环比计算:

# 2. 环比变化率
growth = con.execute("""
    SELECT 
        SUM(CASE WHEN date = ? THEN revenue END) as rev_yesterday,
        SUM(CASE WHEN date = ? THEN revenue END) as rev_day_before,
        ROUND(
            (SUM(CASE WHEN date = ? THEN revenue END) - 
             SUM(CASE WHEN date = ? THEN revenue END)) * 100.0 / 
            NULLIF(SUM(CASE WHEN date = ? THEN revenue END), 0), 
        2) as revenue_growth_pct
    FROM read_csv_auto('/tmp/ecommerce_daily.csv')
""", [yesterday, day_before, yesterday, day_before, day_before]).fetchdf()

# 3. Top 3 商品(按营收)
top_products = con.execute("""
    SELECT product, 
           SUM(revenue) as revenue,
           SUM(orders) as orders,
           COUNT(DISTINCT date) as active_days
    FROM read_csv_auto('/tmp/ecommerce_daily.csv')
    GROUP BY product
    ORDER BY revenue DESC
    LIMIT 3
""").fetchdf()

print("\n📈 环比变化:")
print(growth.to_string(index=False))
print("\n🏆 Top 3 商品:")
print(top_products.to_string(index=False))

性能对比: 同样 100 万行数据,DuckDB SQL 聚合比 pandas 循环快 10-50 倍,且内存占用更低。


六、第四步:Telegram Bot 推送

将报告格式化为美观的 Telegram 消息,通过 Bot API 推送:

import http.client
import json

def send_telegram_message(bot_token, chat_id, message):
    """通过 Telegram Bot API 发送消息"""
    conn = http.client.HTTPSConnection("api.telegram.org")
    payload = json.dumps({
        "chat_id": chat_id,
        "text": message,
        "parse_mode": "HTML"
    })
    headers = {'Content-Type': 'application/json'}
    conn.request("POST", f"/bot{bot_token}/sendMessage", payload, headers)
    res = conn.getresponse()
    print(f"Telegram 状态: {res.status} {res.reason}")
    conn.close()
    return res.status == 200

# 格式化日报消息
msg = f"""📊 <b>电商日报 · {yesterday}</b>

💰 总营收: <b>{daily_report['total_revenue'].values[0]:,.2f} 元</b>
📦 订单数: <b>{daily_report['total_orders'].values[0]}</b> 单
👥 活跃用户: <b>{daily_report['total_users'].values[0]}</b>
🛒 客单价: <b>{daily_report['avg_order_value'].values[0]:.2f} 元</b>

📈 环比昨日: <b>{growth['revenue_growth_pct'].values[0]:+.2f}%</b>

🏆 Top 3 商品:
{chr(10).join([f"  {i+1}. {r['product']}{r['revenue']:,.0f}元" 
                for i, r in top_products.iterrows()])}

🤖 由 DuckDB 自动化日报系统生成
"""

# 调用发送(替换为真实的 token 和 chat_id)
# send_telegram_message("YOUR_BOT_TOKEN", "YOUR_CHAT_ID", msg)

Telegram Bot 配置步骤:

  1. 在 Telegram 搜索 @BotFather,发送 /newbot 创建机器人
  2. 按提示设置机器人名称,获取 bot_token(格式:123456789:ABCdefGHIjklMNOpqrsTUVwxyz
  3. 搜索你的机器人,发送任意消息
  4. 访问 https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates 获取 chat_id

七、第五步:定时调度

两种部署方式,按需选择:

方式一:系统 crontab(推荐生产环境)

# 每天早上 8 点自动执行
0 8 * * * cd ~/duckdb-daily-report && python3 run_report.py >> /var/log/daily_report.log 2>&1

方式二:Python schedule 库(适合开发测试)

import schedule
import time

def job():
    print(f"[{datetime.now()}] 开始执行日报...")
    # 运行查询和推送逻辑
    send_telegram_message("YOUR_BOT_TOKEN", "YOUR_CHAT_ID", msg)
    print("✅ 日报已推送")

schedule.every().day.at("08:00").do(job)

while True:
    schedule.run_pending()
    time.sleep(60)

八、与传统方案的对比

维度传统方案(Excel + VBA)DuckDB 零 ETL 方案
数据准备手动导出 CSV → 导入数据库直接读取 CSV,零 ETL
计算速度10 万行需要 30 秒+100 万行 1-2 秒
维护成本需要 Excel 环境、VBA 调试纯 Python + SQL,跨平台
推送方式人工发送或邮件Telegram Bot 自动推送
部署成本本地电脑 + Excel 授权云服务器 200 元/年
可扩展性单用户、单文件支持多文件 glob、多数据源

核心差异: 传统方案把数据搬来搬去,DuckDB 让数据留在原地被计算。


九、变现路径

这套系统本身就可以成为产品,有三种变现模式:

1. SaaS 订阅服务

让用户上传自己的 CSV 数据,每天自动生成可视化日报推送到 Telegram。

  • 定价: 299 元/月/用户
  • 目标客户: 中小电商、内容创作者、自媒体
  • 获客渠道: 付费频道、技术社区、朋友圈

2. 定制开发服务

帮企业搭建专属日报系统,根据需求定制指标和推送渠道。

  • 定价: 500-2000 元/次
  • 目标客户: 有 5-50 人的电商团队
  • 交付周期: 1-3 天

3. 数据产品订阅

用公开数据源(Kaggle、政府开放数据)生成每日行业报告,在付费频道售卖。

  • 定价: 99 元/月
  • 目标客户: 投资者、行业分析师
  • 差异化: 独家 DuckDB 分析视角

十、进阶技巧

1. 多文件合并读取

当数据分散在多个 CSV 文件中时,DuckDB 支持 glob 模式直接读取:

-- 读取当月所有 CSV 文件,自动合并
SELECT date, SUM(revenue) as daily_revenue
FROM read_csv_auto('/data/sales/2026-08/*.csv')
GROUP BY date
ORDER BY date;

2. 性能优化:并行读取

DuckDB 自动利用多核 CPU 并行读取大文件。可以通过设置强制并行度:

con = duckdb.connect(config={
    'max_threads': 4,
    'threads_per_query': 4
})

3. 错误处理与告警

在生产环境中,建议添加异常捕获和告警机制:

try:
    daily_report = con.execute(...).fetchdf()
    send_telegram_message(token, chat_id, msg)
except Exception as e:
    error_msg = f"❌ 日报生成失败: {str(e)}"
    send_telegram_message(token, chat_id, error_msg)
    raise

总结

DuckDB 让报表系统回归本质:数据在哪里,计算就在哪里发生。不需要 ETL 管道,不需要数据库服务器,一个 CSV 文件 + 十行 SQL 就能完成从原始数据到推送通知的全流程。

这套系统的价值不在于技术本身,而在于可复制性——一旦为第一个客户搭建完成,就可以快速复制给更多客户,边际成本趋近于零。

💡 更多 DuckDB 实战技巧 → duckdblab.org

📺 Watch video tutorials → Olap Studio YouTube

Subscribe for more DuckDB & AI automation tutorials

使用 Hugo 构建
主题 StackJimmy 设计

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

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

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