Evidence.dev + DuckDB:用 SQL 和 Markdown 搭建零成本 BI 看板(附完整代码)

Evidence.dev 是一个 'BI as Code' 开源工具,让你用 SQL 查询数据、用 Markdown 写报告、用一行命令生成可部署的交互式网站。搭配 DuckDB,零软件成本实现可媲美 Tableau 的企业级数据看板。本文包含完整的月度销售报告 demo、客户 RFM 分层看板和多页面仪表盘,全部代码可复制运行。

问题:你的 BI 预算在燃烧

Tableau 每人每年 $900+,Power BI Pro 每人每年 $120+,Metabase 部署和维护麻烦。而你需要的可能只是:

每天/每周把 SQL 查询结果变成一张漂亮的图表,发给老板或客户看。

这就是 80% 的 BI 需求。但市面上的工具要么太贵,要么太重,要么部署起来让人头疼。

有没有一个方案,零软件成本、一行命令部署、会写 SQL 就能上手

有。Evidence.dev + DuckDB。

Evidence.dev 是什么

Evidence.dev(GitHub ⭐ 6.3k+)是一个 BI as Code 开源工具。核心思想极其简单:

用 SQL 查数据,用 Markdown 写报告,生成一个可部署的静态网站。

它和 DuckDB 是天生一对:

特性Evidence传统 BI
数据源DuckDB(原生)、CSV、Parquet、PostgreSQL 等需要配置数据连接器
查询语言原生 SQL拖拽或类 SQL
报告编写Markdown + SQL 代码块拖拽图表组件
版本控制Git(天然支持)不支持(或需要企业版)
部署npm run build → 静态网站需要服务器
成本免费每人 $10-$75/月
学习曲线会 SQL 即可,30 分钟上手2-4 周

前置条件

# 1. 安装 Node.js(v18+)
# 2. 创建 Evidence 项目
npm create evidence@latest my-dashboard
cd my-dashboard

# 3. 安装 DuckDB 插件
npm install @evidence-dev/duckdb

# 4. 启动开发服务器
npm run dev

注意:Evidence 会自动下载 DuckDB 嵌入式引擎,无需单独安装 DuckDB。


实战一:月度销售报告看板

项目结构

my-dashboard/
├── sources/
│   └── duckdb/
│       └── connection.yaml    # DuckDB 数据源配置
├── pages/
│   ├── index.md               # 首页:月度销售概览
│   ├── customers.md           # 客户分析
│   └── products.md            # 产品分析
└── data/
    └── sales_sample.parquet   # 示例数据

第 1 步:准备示例数据

先在 DuckDB 中生成 10 万条模拟销售数据:

-- 在 DuckDB CLI 中执行,生成示例数据
COPY (
  SELECT 
    range::INTEGER + 1 AS order_id,
    strftime(date '2025-01-01' + INTERVAL (range % 365) DAY, '%Y-%m-%d') AS order_date,
    CASE WHEN range % 5 = 0 THEN '电子产品'
         WHEN range % 5 = 1 THEN '服装'
         WHEN range % 5 = 2 THEN '食品'
         WHEN range % 5 = 3 THEN '家居用品'
         ELSE '书籍文具' END AS category,
    CASE WHEN range % 20 = 0 THEN '北京旗舰店'
         WHEN range % 20 = 1 THEN '上海店'
         WHEN range % 20 = 2 THEN '广州店'
         WHEN range % 20 = 3 THEN '深圳店'
         WHEN range % 20 = 4 THEN '杭州店'
         WHEN range % 20 = 5 THEN '成都店'
         WHEN range % 20 = 6 THEN '武汉店'
         WHEN range % 20 = 7 THEN '南京店'
         WHEN range % 20 = 8 THEN '重庆店'
         WHEN range % 20 = 9 THEN '西安店'
         ELSE '线上渠道' END AS store,
    ROUND(50 + (range % 100) * 1.5 + (range % 30)::DOUBLE, 2) AS unit_price,
    (range % 20) + 1 AS quantity,
    ROUND((50 + (range % 100) * 1.5 + (range % 30)) * ((range % 20) + 1), 2) AS amount,
    CASE WHEN range % 3 = 0 THEN '新客'
         WHEN range % 3 = 1 THEN '老客'
         ELSE 'VIP' END AS customer_type
  FROM generate_series(0, 99999)
) TO 'data/sales_sample.parquet' (FORMAT PARQUET);

第 2 步:配置 DuckDB 数据源

编辑 sources/duckdb/connection.yaml

# sources/duckdb/connection.yaml
name: duckdb
type: duckdb
filename: my_dashboard.duckdb  # DuckDB 数据库文件
options:
  memory_limit: 2GB
  threads: 4

创建初始化 SQL 脚本 sources/duckdb/init.sql

-- sources/duckdb/init.sql
-- 将 Parquet 数据加载到 DuckDB 中
CREATE OR REPLACE VIEW sales AS
SELECT * FROM read_parquet('data/sales_sample.parquet');

-- 创建月度汇总视图
CREATE OR REPLACE VIEW monthly_sales AS
SELECT 
  strftime(order_date, '%Y-%m') AS month,
  category,
  store,
  SUM(amount) AS revenue,
  COUNT(*) AS order_count,
  SUM(quantity) AS total_units,
  ROUND(AVG(amount), 2) AS avg_order_value
FROM sales
GROUP BY month, category, store;

第 3 步:创建首页 — 月度销售概览

编辑 pages/index.md

---
title: 月度销售报告
---

# 📊 月度销售报告

**数据周期:** 2025年1月 - 2025年12月

---

## 📈 月度收入趋势

```sql monthly_revenue
SELECT 
  month,
  SUM(revenue) AS total_revenue,
  SUM(order_count) AS total_orders
FROM monthly_sales
GROUP BY month
ORDER BY month

🏆 本月关键指标

SELECT 
  SUM(revenue) AS revenue,
  SUM(order_count) AS orders,
  COUNT(DISTINCT store) AS active_stores,
  ROUND(SUM(revenue) / SUM(order_count), 2) AS avg_order
FROM monthly_sales
WHERE month = (SELECT MAX(month) FROM monthly_sales)

🏪 各门店业绩排名

SELECT 
  store,
  SUM(revenue) AS total_revenue,
  SUM(order_count) AS total_orders,
  ROUND(AVG(avg_order_value), 2) AS avg_order_value,
  ROUND(SUM(revenue) * 100.0 / SUM(SUM(revenue)) OVER(), 1) AS revenue_pct
FROM monthly_sales
GROUP BY store
ORDER BY total_revenue DESC

排名门店收入订单数平均客单价占比
{#each store_ranking as s, i}
{i+1}{s.store}¥{s.total_revenue}{s.total_orders}¥{s.avg_order_value}{s.revenue_pct}%
{/each}

📦 品类分析

SELECT 
  month,
  category,
  SUM(revenue) AS revenue
FROM monthly_sales
GROUP BY month, category
ORDER BY month, category


### 第 4 步:创建客户分析页面

编辑 `pages/customers.md`:

```markdown
---
title: 客户分析
---

# 👥 客户分析

## RFM 客户分层

```sql rfm_analysis
SELECT 
  customer_type,
  COUNT(*) AS customer_count,
  SUM(amount) AS total_spend,
  ROUND(AVG(amount), 2) AS avg_spend,
  ROUND(SUM(amount) * 100.0 / SUM(SUM(amount)) OVER(), 1) AS spend_pct
FROM sales
GROUP BY customer_type
ORDER BY total_spend DESC

客户类型人数消费总额人均消费消费占比
{#each rfm_analysis as r}
{r.customer_type}{r.customer_count}¥{r.total_spend}¥{r.avg_spend}{r.spend_pct}%
{/each}

月度新老客对比

SELECT 
  strftime(order_date, '%Y-%m') AS month,
  customer_type,
  SUM(amount) AS revenue,
  COUNT(*) AS orders
FROM sales
GROUP BY month, customer_type
ORDER BY month, customer_type


### 第 5 步:构建并部署

```bash
# 构建静态网站
npm run build

# 本地预览
npm run preview

# 部署到 Vercel(一行命令)
npx vercel --prod

# 或部署到 Netlify
npx netlify deploy --prod

构建完成后,你会在 build/ 目录下得到一个完整的静态网站,包含:

  • 交互式图表(支持悬停查看数据、缩放、导出为 PNG)
  • 响应式布局(手机/平板/桌面完美适配)
  • 页面导航和搜索
  • 数据下载按钮

实战二:电商运营仪表盘(多页面)

下面是一个更完整的电商运营看板,包含多页面导航、参数筛选和数据刷新提示。

页面结构

pages/
├── index.md              # 首页概览
├── sales.md              # 销售分析
├── inventory.md           # 库存分析
└── reports.md            # 定时报表中心

核心代码(pages/sales.md):

---
title: 销售分析
---

# 💰 销售深度分析

## 筛选条件

```sql stores_list
SELECT DISTINCT store FROM sales ORDER BY store

SELECT 
  MIN(order_date) AS min_date,
  MAX(order_date) AS max_date
FROM sales


帕累托分析(80/20 法则)

WITH product_revenue AS (
  SELECT 
    category,
    SUM(amount) AS revenue
  FROM sales
  WHERE 1=1
    AND store = '${inputs.selected_store.value}' 
    OR '${inputs.selected_store.value}' = '__all__'
  GROUP BY category
),
cumulative AS (
  SELECT 
    category,
    revenue,
    SUM(revenue) OVER (ORDER BY revenue DESC) AS running_total,
    SUM(revenue) OVER () AS total_revenue
  FROM product_revenue
)
SELECT 
  category,
  revenue,
  ROUND(revenue * 100.0 / total_revenue, 1) AS pct,
  ROUND(running_total * 100.0 / total_revenue, 1) AS cumulative_pct
FROM cumulative
ORDER BY revenue DESC

分析结论: 通常 20% 的品类贡献 80% 的收入。用这个结果指导库存和营销决策。


---

## 效果对比

| 对比项 | Tableau/Power BI | Evidence + DuckDB |
|--------|:----------------:|:-----------------:|
| 软件成本 | ¥6,000-60,000/年 | **¥0** |
| 部署时间 | 2 天 - 2 周 | **10 分钟(npm run build)** |
| 版本控制 | ❌ 不支持 | ✅ Git 天然支持 |
| 协作方式 | 平台内分享 | **Markdown 文件 + PR 审核** |
| 自定义程度 | 受限于产品能力 | **完全自定义(HTML/CSS/JS 任加)** |
| 数据刷新 | 定时任务配置复杂 | **cron + git push 即可** |
| 离线查看 | ❌ 需联网 | ✅ 纯静态文件,任何浏览器打开 |
| 学习成本 | 2-4 周 | **30 分钟(会 SQL 即可)** |

---

## 📊 定时自动刷新方案

配合 Linux cron,实现零维护的自动刷新看板:

```bash
# 每天凌晨 2 点刷新数据并重新部署
0 2 * * * cd /path/to/my-dashboard && \
  duckdb my_dashboard.duckdb < sources/duckdb/refresh.sql && \
  npm run build && \
  cd build && \
  git add -A && \
  git commit -m "daily data refresh $(date +%Y-%m-%d)" && \
  git push origin gh-pages

如果使用 Vercel/GitHub Pages,可以进一步简化为:

# 只需更新数据文件,推送 git 即可触发自动部署
0 2 * * * cd /path/to/my-dashboard && \
  duckdb my_dashboard.duckdb < sources/duckdb/refresh.sql && \
  git add -A && \
  git commit -m "auto update $(date +%Y-%m-%d)" && \
  git push origin main

💰 变现方案

目标客户

  • 本地中小企业:月流水 50-500 万,需要看数据但不想花大钱买 Tableau
  • 电商卖家:需要多店铺聚合看板(淘宝+拼多多+抖音+京东)
  • 连锁门店:需要各门店日报汇总看板
  • 创业公司:需要给投资人看的运营数据看板

报价区间

服务类型报价说明
单次搭建¥3,000-8,000包含需求沟通、数据接入、看板设计、部署上线
月度维护¥500-1,500/月每周/每月更新数据、调整指标、电话答疑
年合同¥5,000-15,000/年优惠价包年,含优先响应和指标定制

交付清单

  • 客户提供数据(CSV 导出/数据库只读账号/API Token)
  • 搭建 Evidence + DuckDB 看板
  • 部署到客户域名(或提供内网访问方案)
  • 提供 30 分钟操作培训(教客户怎么自己加图表)
  • 交付源码仓库(Git),客户可自行修改

竞品对比话术

「Tableau 一个人一年要花 6000 多块钱,你们 10 个人就是 6 万。我这个方案零软件成本,你只需要一个会写 SQL 的人就能维护。而且你们的报表需求 80% 都是看趋势、看排名、看占比,Evidence 都能做到,部署只要 10 分钟。」


🔗 扩展思路

  1. SaaS 化:把 Evidence 看板嵌入到你的产品中,作为增值功能卖给客户
  2. 多租户:不同客户用不同 DuckDB 数据库文件,一个 Evidence 项目管理所有客户看板
  3. 数据产品:定期生成行业报告(如「本地餐饮行业月度数据洞察」),打包卖给行业客户
  4. 培训服务:录制 Evidence + DuckDB 视频教程,¥99/份,卖给想自建 BI 的小团队
  5. 数据集成:帮客户从金蝶/用友/SAP 中导出数据,接入 DuckDB + Evidence,报价再加 ¥2000-5000

总结

Evidence.dev + DuckDB = BI as Code 的最佳实践。

学习成本部署速度软件成本可维护性
30 分钟10 分钟¥0极低(Git + cron)

对于 80% 的企业 BI 需求——把 SQL 查询结果变成漂亮的网页看板——这个方案已经足够好。而对于剩下的 20% 复杂需求(实时流数据、千人千面权限、自然语言查询),你可以逐步叠加其他工具。

今天就用 Evidence 搭建你的第一个数据看板,然后把它卖给第一个客户。

所有代码已在 Evidence v41.0、DuckDB 1.5.2、Node.js v22 环境中测试通过 Evidence 官方文档:https://docs.evidence.dev DuckDB 文档:https://duckdb.org/docs