跳转至

Evidence:SQL + Markdown 生成数据报告网站

为什么要学 Evidence

数据分析的最后一公里问题——你写了 SQL 得到了结果,但要把它变成好看的报告、仪表板或数据文档,往往需要在 BI 工具中反复拖拽配置,或者用 Python 写大量可视化代码。

Evidence 的理念是:用写文档的方式做数据报告。你只需要在 Markdown 文件中嵌入 SQL 查询,Evidence 会自动执行查询、渲染图表、生成一个静态网站。就像 "Jekyll for data reporting"。

核心价值: - 写 Markdown + SQL 就能生成专业级数据报告网站 - 代码即报告:可以用 Git 做版本管理、Code Review - 零配置图表:SQL 结果自动匹配最佳图表类型 - 部署简单:输出静态文件,可以部署到任何地方 - 支持参数化报告和交互式组件

适用场景: - 周报/月报自动化 - 公司内部数据仪表板 - 面向客户的数据文档 - KPI 监控页面 - 数据团队的分析博客


核心概念

白话解释

概念白话说明
Page一个 Markdown 文件就是一个页面/报告
SQL Query在 Markdown 中用代码块写 SQL,Evidence 自动执行并命名结果集
Component内置的可视化组件(图表、表格、KPI卡片等)
Data Source连接的数据源(PostgreSQL、BigQuery、DuckDB等)
Templated Page参数化页面,一个模板生成多个页面
Universal SQLEvidence 支持的 SQL 方言,跨数据源兼容
Build将 Markdown 编译为静态 HTML 网站的过程

Evidence 的工作流程

编写 Markdown + SQL
Evidence 开发服务器 (实时预览)
执行 SQL → 获取数据 → 渲染图表 → 生成页面
evidence build → 静态网站 → 部署

支持的数据源

数据源说明
DuckDB内置,可直接查询 CSV/Parquet
PostgreSQL生产数据库
MySQL生产数据库
BigQueryGoogle 云数据仓库
Snowflake云数据仓库
SQLite轻量级本地数据库
DatabricksSpark SQL
Trino/Presto联邦查询
CSV/Parquet本地文件(通过 DuckDB)

安装配置

系统要求

  • Node.js 18+
  • npm 或 pnpm

创建项目

# 使用官方脚手架
npx degit evidence-dev/template my-report
cd my-report

# 安装依赖
npm install

# 启动开发服务器
npm run dev

浏览器打开 http://localhost:3000 即可看到示例报告。

配置数据源

# 在开发模式下访问 http://localhost:3000/settings
# 可以通过 UI 配置数据源

# 或手动编辑 evidence.plugins.yaml

手动配置示例(PostgreSQL):

# evidence.plugins.yaml
datasources:
  - postgres:
      host: localhost
      port: 5432
      database: analytics
      user: readonly
      password: ${POSTGRES_PASSWORD}  # 使用环境变量

本地 CSV 文件(使用内置 DuckDB):

# 将 CSV 文件放在 sources/ 目录下
mkdir -p sources/local
cp sales_data.csv sources/local/
# Evidence 会自动将它注册为可查询的表

快速上手

第一个报告页面

创建 pages/index.md

---
title: 销售仪表板
---

# 销售概览

```sql sales_summary
SELECT 
    date_trunc('month', order_date) as month,
    COUNT(*) as order_count,
    SUM(amount) as revenue
FROM orders
GROUP BY 1
ORDER BY 1
```

<LineChart 
    data={sales_summary} 
    x=month 
    y=revenue 
    title="月度收入趋势"
/>

## 关键指标

<BigValue 
    data={sales_summary} 
    value=revenue 
    title="总收入"
    fmt="$#,##0"
/>

<BigValue 
    data={sales_summary} 
    value=order_count 
    title="总订单数"
/>

## 明细数据

<DataTable data={sales_summary} rows=10 />

使用本地 CSV 数据

将 CSV 放入 sources/ 目录后:

```sql csv_data
SELECT * FROM local.sales_data
WHERE region = 'East'
ORDER BY revenue DESC
LIMIT 20
```

<BarChart data={csv_data} x=product y=revenue />

页面路由

pages/
├── index.md            → /
├── sales.md            → /sales
├── customers/
│   ├── index.md        → /customers
│   └── [id].md         → /customers/123 (参数化)
└── reports/
    └── monthly.md      → /reports/monthly

进阶用法

1. 丰富的图表类型

## 折线图
<LineChart data={query_result} x=date y=value />

## 面积图
<AreaChart data={query_result} x=date y={["sales", "costs"]} />

## 柱状图
<BarChart data={query_result} x=category y=count sort=true />

## 散点图
<ScatterPlot data={query_result} x=price y=rating size=volume />

## 饼图
<PieChart data={query_result} name=category value=percentage />

## 漏斗图
<FunnelChart data={funnel_data} name=stage value=users />

## 热力图
<Heatmap data={query_result} x=day y=hour value=count />

## 地图
<USMap data={state_data} state=state_name value=population />

2. 参数化报告

---
title: 客户详情
---

# 客户 {params.customer_id} 的详情

```sql customer_info
SELECT * FROM customers
WHERE customer_id = '${params.customer_id}'
```

```sql customer_orders
SELECT * FROM orders
WHERE customer_id = '${params.customer_id}'
ORDER BY order_date DESC
```

<DataTable data={customer_info} />

## 订单历史

<LineChart data={customer_orders} x=order_date y=amount />

文件名为 [customer_id].md,访问 /customers/CUST001 时自动传参。

3. 交互式筛选

```sql all_regions
SELECT DISTINCT region FROM sales ORDER BY region
```

<Dropdown 
    name=selected_region 
    data={all_regions} 
    value=region 
    title="选择区域"
/>

```sql filtered_sales
SELECT * FROM sales
WHERE region = '${inputs.selected_region}'
```

<BarChart data={filtered_sales} x=product y=revenue />

4. 条件渲染

```sql revenue_check
SELECT SUM(revenue) as total FROM sales WHERE month = current_month
```

{#if revenue_check[0].total > 1000000}
  <Alert status="success">本月收入已超百万!</Alert>
{:else}
  <Alert status="warning">本月收入未达标</Alert>
{/if}

5. 循环生成内容

```sql departments
SELECT DISTINCT department FROM employees ORDER BY department
```

{#each departments as dept}
  ## {dept.department} 部门

  ```sql dept_stats
  SELECT COUNT(*) as headcount, AVG(salary) as avg_salary
  FROM employees WHERE department = '${dept.department}'
  ```

  - 人数: {dept_stats[0].headcount}
  - 平均薪资: {fmt(dept_stats[0].avg_salary, '$#,##0')}
{/each}

6. 自定义样式和布局

<Grid cols=3>
    <BigValue data={kpi1} value=metric title="DAU" />
    <BigValue data={kpi2} value=metric title="收入" fmt="$#,##0" />
    <BigValue data={kpi3} value=metric title="转化率" fmt="0.0%" />
</Grid>

<Tabs>
    <Tab label="日视图">
        <LineChart data={daily} x=date y=value />
    </Tab>
    <Tab label="周视图">
        <LineChart data={weekly} x=week y=value />
    </Tab>
</Tabs>

7. 构建与部署

# 构建静态网站
npm run build

# 输出在 build/ 目录,可以部署到:
# - Vercel
# - Netlify
# - GitHub Pages
# - 任何静态文件服务器

# 预览构建结果
npm run preview

部署到 Vercel:

npm i -g vercel
vercel --prod

8. 定时刷新数据

# 使用 GitHub Actions 定时重建
# .github/workflows/refresh.yml
name: Refresh Report
on:
  schedule:
    - cron: '0 8 * * *'  # 每天早8点
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm run build
        env:
          POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
      - uses: actions/upload-pages-artifact@v3
        with:
          path: build

常见问题

Q1: Evidence 和 Superset/Metabase 有什么区别

维度EvidenceSuperset/Metabase
工作方式Code as Report拖拽式 BI
版本管理Git 原生数据库存储配置
协作方式Pull Request在线编辑
部署静态文件需要后端服务器
实时交互有限
定制化高(写代码)中(配置)
学习曲线会 SQL + Markdown 即可需要学 BI 工具

Q2: 数据安全性如何保证

  • 构建时执行 SQL,输出是静态 HTML
  • 生产环境不直连数据库
  • 敏感数据可以在查询层面做过滤
  • 支持环境变量管理数据库凭证
  • 可以加访问控制(Vercel password protection等)

Q3: 能处理大数据量吗

  • Evidence 在构建时执行查询,不受浏览器内存限制
  • 建议在 SQL 层做聚合,不要 SELECT * 百万行
  • DuckDB 数据源可以高效处理 GB 级 CSV/Parquet

Q4: 如何调试 SQL 错误

# 开发模式下,错误会直接显示在页面上
npm run dev

# 查看详细日志
npm run dev -- --verbose

Q5: 能嵌入到现有网站吗

Evidence 输出标准 HTML/CSS/JS,可以: - 使用 iframe 嵌入 - 直接部署为子路径 - 通过反向代理集成


参考资源

资源链接
官方网站https://evidence.dev
GitHub 仓库https://github.com/evidence-dev/evidence
文档https://docs.evidence.dev
组件库https://docs.evidence.dev/components
示例报告https://evidence.dev/examples
社区模板https://github.com/evidence-dev/template

小结: Evidence 把"写报告"这件事回归到了工程师最舒服的方式——写代码。SQL 负责数据逻辑,Markdown 负责叙事结构,组件负责可视化。如果你厌倦了在 BI 工具中反复拖拽,或者想让数据报告享受 Git 工作流的所有好处(版本管理、Code Review、CI/CD),Evidence 是目前最成熟的选择。