数据分析师每天要处理一堆”陌生文件”——同事甩过来的 .parquet、客户给的 .xlsx、抓取的 .csv / .ndjson。传统流程:开 Jupyter → 装 pandas + pyarrow → pd.read_parquet() → 查列名 → 写 groupby → 看结果。装环境就 5 分钟,数据量稍大 pandas 还会 OOM。
浏览器里跑 DuckDB SQL 是这两年才能做到的方案:DuckDB 团队把整个 C++ 引擎编译成 WebAssembly(约 35 MB wasm),分析师拖文件进网页就能写 SQL,结果毫秒级返回,不需要安装、不需要后端、文件不离开本地。
浏览器内 OLAP 引擎是什么
DuckDB 是过去几年最火的「单机分析数据库」,核心特点是:
- 列存 + 向量化执行:聚合查询比 SQLite / Postgres 快几十倍
- 零部署:单文件二进制(CLI)或单文件 wasm(浏览器),不需要 server / 配置
- 格式无感:直接 SQL 查 Parquet / CSV / JSON,不需要先 LOAD INTO 一张表
- PostgreSQL 兼容语法:CTE / 窗口函数 /
QUALIFY/GROUP BY ALL/ lambda 都支持
把它编译成 wasm 之后,浏览器就成了一个本地 OLAP 节点。和把 SQLite 编进 wasm 不同的是——SQLite 面向 OLTP(小事务、行存),DuckDB 面向 OLAP(大查询、列存),后者才是「数据分析」该用的数据库。
5 个实战场景
1. 看 Parquet 文件里有什么
拿到一个陌生 Parquet 想知道列、类型、行数:
-- 列结构 + 类型
DESCRIBE big.parquet;
-- 行数和样本
SELECT COUNT(*) AS rows FROM big.parquet;
SELECT * FROM big.parquet LIMIT 10;
工具自动跑了第三句,但前两句你可以手动追加补充。DESCRIBE 是 DuckDB 特有语法,比 PRAGMA table_info 输出更友好。
2. CSV 当成数据库查(替代 Excel 透视表)
老板甩了一份 sales.csv 想知道各区域销售:
-- 等价于 Excel 透视表「区域 → 月 → 总销售额」
SELECT
region,
date_trunc('month', sale_date) AS month,
SUM(amount) AS gmv,
COUNT(*) AS orders,
AVG(amount)::DECIMAL(10,2) AS aov
FROM sales
GROUP BY ALL
ORDER BY region, month;
GROUP BY ALL 是 DuckDB / Snowflake 的便利语法——自动按所有非聚合列分组,省了把 SELECT 列表的前几列再写一遍。Excel 透视表点 10 下能搞定的活,SQL 4 行写完,而且重复使用、可改、可版本控制。
3. 多文件 JOIN(跨数据源关联)
events.parquet + users.csv + categories.json:
SELECT
u.country,
c.name AS category,
COUNT(*) AS events,
COUNT(DISTINCT e.user_id) AS uv
FROM events e
JOIN users u ON e.user_id = u.id
JOIN categories c ON u.country_category_id = c.id
WHERE e.event_name = 'purchase'
AND e.ts >= '2026-01-01'
GROUP BY 1, 2
QUALIFY ROW_NUMBER() OVER (PARTITION BY u.country ORDER BY uv DESC) <= 3;
QUALIFY 是窗口函数版本的 WHERE——在窗口计算之后过滤,省掉一层子查询。这种”每个国家 Top 3 类目”的查询是分析师高频场景,用 SQL 几行解决,pandas 写起来要套一坨 groupby + apply + nlargest。
4. Excel 多 sheet 跨表分析
拖入 sales.xlsx(Q1 + Q2 双 sheet):
-- 跨 sheet UNION + 同比
WITH all_sales AS (
SELECT 'Q1' AS quarter, * FROM sales_q1
UNION ALL
SELECT 'Q2', * FROM sales_q2
)
SELECT
region,
SUM(total) FILTER (WHERE quarter = 'Q1') AS q1_gmv,
SUM(total) FILTER (WHERE quarter = 'Q2') AS q2_gmv,
ROUND((SUM(total) FILTER (WHERE quarter = 'Q2'))
/ NULLIF(SUM(total) FILTER (WHERE quarter = 'Q1'), 0) * 100 - 100, 1) AS qoq_pct
FROM all_sales
GROUP BY region
ORDER BY q2_gmv DESC;
FILTER (WHERE ...) 是 SQL 标准的「条件聚合」,比 SUM(CASE WHEN ... THEN x ELSE 0 END) 更可读。Excel 里要做这种环比要建额外的辅助列 + VLOOKUP,SQL 一段搞定。
5. 数据质量自检(异常值 / 空值 / 重复)
-- 缺失率
SELECT
COUNT(*) AS total,
COUNT(*) - COUNT(email) AS email_missing,
COUNT(*) - COUNT(phone) AS phone_missing,
ROUND((1.0 - COUNT(email)::DOUBLE / COUNT(*)) * 100, 2) AS email_missing_pct
FROM users;
-- 重复主键
SELECT id, COUNT(*) AS dup_count
FROM users
GROUP BY id
HAVING COUNT(*) > 1;
-- 数值异常
SELECT
MIN(amount) AS min_v,
MAX(amount) AS max_v,
AVG(amount) AS avg_v,
STDDEV(amount) AS sd,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY amount) AS median,
PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY amount) AS p99
FROM sales;
数据交付前的”sanity check”——每条 30 秒跑完,比 Excel 一列列点筛选快得多。
Parquet vs CSV vs Excel:选什么
| 维度 | Parquet | CSV | Excel (xlsx) |
|---|---|---|---|
| 体积 | ★★★★★(列存 + 压缩) | ★★ | ★★★(zip 内是 XML) |
| 类型 | ★★★★★(schema 内嵌) | ★(要嗅探) | ★★★★(cell type) |
| 跨语言 | ★★★★★ | ★★★★★ | ★★★ |
| 浏览器解析速度 | ★★★★★ | ★★★★ | ★★(zip 解压慢) |
| 给非技术同事看 | ★(要工具) | ★★★★ | ★★★★★ |
| 大文件友好 | ★★★★★ | ★★ | ★(行数限制 + 性能) |
建议:
- 数据存储 / 数据交付(工程间):Parquet(小、快、类型准)
- 临时给同事看:CSV(任何人都能打开)
- 运营 / 财务交付:Excel(带格式、能直接看)
- 本工具的 sweet spot:拿到任意三种之一,浏览器直接 SQL 分析,省去格式转换
性能边界
| 场景 | 表现 |
|---|---|
| 启动加载(首次) | 35 MB wasm 下载 + 实例化,~5-10 秒(看带宽) |
| Parquet 100 万行 | 加载 1-3 秒,聚合查询 < 200 ms |
| CSV 50 万行 | 加载 + 推断类型 1-2 秒,查询 < 100 ms |
| Excel 10 万行 30 列 | SheetJS 解析 + 转 CSV 30-60 秒(Worker 内不卡 UI),之后查询毫秒级 |
| 极限 | 单文件 1-2 GB,超过会受 V8 堆限制(2-4 GB) |
V8 单页堆 OOM 时浏览器会刷新页面,所以重要的查询结果记得及时导出。
同款”先看一眼”工具
只想瞄一眼 Parquet 文件的 schema 和前 N 行、不需要写 SQL,用更轻量的 Parquet 在线预览(基于 hyparquet,包体积 50 KB,秒开)。两个工具配合:预览器看结构 → SQL 工具做分析,覆盖整条「看一眼 → 探索 → 输出」链路。
局限
- 不持久化:刷新丢编辑器内容、丢已注册的表(这是本地优先 + 隐私设计的取舍)
- 不联网读外部 URL:DuckDB 的
httpfs扩展默认未启用(要联网就跟”本地隐私”冲突) - 大于 V8 堆的文件:去用本地 DuckDB CLI,相同 SQL 直接复制能跑
- 复杂特征工程:要调用 Python 生态(sklearn / numpy)的还是 Jupyter