.db 文件是 SQLite 数据库——手机 App 的本地缓存、Electron 应用的设置存档、运营导出的数据备份,几乎都是这个格式。问题是看它得装本地工具,敏感文件还得担心上传安全。浏览器里跑 sql.js(SQLite 编译成 WebAssembly),文件不出本地、不装东西,几秒钟看到全部内容。这篇讲完整工作流,从文件识别到导出。
一眼辨认 SQLite 文件
未知后缀的文件不知道是不是 SQLite?看文件头 16 字节就行。SQLite 3.x 的格式硬编码:
SQLite format 3\0
53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00
命令行:
xxd unknown.bin | head -1
file unknown.bin # macOS / Linux 自动识别
不是这个头?常见情况:被 ZIP / GZ 包了、是 SQLCipher 加密库(首字节随机)、或者根本不是 SQLite(可能是 LevelDB、BerkeleyDB、或某厂商私有格式)。默认 sql.js 不支持 SQLCipher,要带加密扩展的特殊构建。
拖进浏览器就开
工具页中央有个拖拽框,把文件拖进去(或点”选择文件”)就解析。整个过程:
- 浏览器
FileReader读字节到Uint8Array - sql.js(WASM 660 KB,首次加载后缓存)反序列化到内存数据库
- 查询
sqlite_master拉出表 / 视图 / 索引 schema - 渲染左侧树,自动选第一个表预览
没有任何网络请求——可以断网用,DevTools 的 Network 面板空空如也。这是核心卖点:处理客户备份、用户隐私表、内部审计 dump,不存在”上传到陌生服务器”的合规风险。
第一件事:扫 schema
打开任何陌生数据库,先理解它有什么:
| 看什么 | 在哪看 |
|---|---|
| 表 / 视图 / 索引列表 | 左侧 schema 树,已分组 |
| 每张表的行数 | 表名后的数字(首次加载时跑了 COUNT(*)) |
| 列名、类型、主键 | 在搜索框输入列名,命中的列展开显示 |
| 完整建表语句 | 实际不用看 sql 字段——结果表头会推断显示 INTEGER/TEXT/REAL/BLOB |
经验:行数 > 10 万的”宽表”(很多 TEXT 列)通常是日志或事件流;行数极少(< 50)的表多是配置 / 字典;带 _log _history 后缀的多是审计表。
SELECT 别忘 LIMIT
第一条查询永远先加 LIMIT。点表名工具会自动填入:
SELECT * FROM "users" ORDER BY "id" DESC LIMIT 100;
结果表的列头同时显示列名 + 推断类型(INTEGER 偏右对齐、TEXT 左对齐、BLOB 灰色提示长度)。如果列声明是 REAL 但数据全是整数(如 1899.00 在 JS 里折成 1899),头部仍会按 schema 标 REAL——准确。
常见过滤模式:
-- 时间区间(SQLite 把日期当字符串比,ISO 8601 字典序天然有效)
SELECT * FROM orders WHERE ordered_at >= '2025-01-01' AND status = 'paid';
-- 模糊匹配(注意 LIKE 是大小写敏感;SQLite 默认 NOCASE 只对 ASCII)
SELECT * FROM users WHERE email LIKE '%@example.com';
-- IN 列表
SELECT * FROM products WHERE category IN ('手机', '电脑');
JOIN + 聚合:常见的两张表问题
最常被问的查询模式:“这些用户里谁买得最多?”
SELECT u.name, u.city,
COUNT(o.id) AS order_count,
SUM(o.total) AS revenue
FROM users u
LEFT JOIN orders o
ON o.user_id = u.id
AND o.status IN ('paid','shipped','delivered')
GROUP BY u.id
ORDER BY revenue DESC NULLS LAST
LIMIT 20;
几个细节:
LEFT JOIN而不是JOIN才能保留没下过单的用户- 过滤条件放
ON而不是WHERE,否则左连接被退化成内连接 NULLS LAST让 0 单的人排到最后(SQLite 3.30+ 支持)- 大表上 GROUP BY 慢?先看左侧索引列表,是否有
(user_id, status)复合索引
修改数据:务必”下载 DB”
INSERT / UPDATE / DELETE / CREATE TABLE / DROP INDEX 都能跑,影响行数会显示在状态栏。但改的是浏览器内存里的数据库——关闭页面、刷新、加载新文件都会丢失。
工具会亮起橙色提示条提醒:「有未保存的修改 · 仅存在浏览器内存中」。点条上的「下载 DB」把当前状态导出成新 .db 文件(文件名带时间戳,不会和原文件混淆)。原文件不会被改写——浏览器没有写本地文件的能力,整个工作流是”读原文件 → 在内存里改 → 下载新文件”。
如果跑了多条语句中途某条失败:之前已成功的 INSERT/CREATE 保留(SQLite 默认 auto-commit),不会自动回滚。状态栏会显示「已成功 N 条 · 第 K 条失败」,schema 树和补全也会按已发生的改动刷新。要全有全无,自己包一层 BEGIN; ... COMMIT;,失败时 ROLLBACK。
导出给非技术同事
| 格式 | 何时用 |
|---|---|
| CSV | 给运营 / 业务用 Excel 看(带 UTF-8 BOM,中文不乱码) |
| JSON | 喂给前端 mock 数据 / 后续脚本处理(BLOB 自动转 base64) |
| INSERT SQL | 把查询结果搬到另一个数据库(导出前会问目标表名) |
| 复制(TSV) | 直接粘到 Excel / Numbers / 飞书表格,列对齐 |
INSERT 导出会用 prompt() 让你确认目标表名——不靠正则猜,避免把 SELECT u.name, o.amount FROM users u JOIN orders o 的结果写成 INSERT INTO users (...) 这种半对半错的脚本。
CSV 在 Excel 里仍乱码?多半是用了 WPS 老版或 LibreOffice,它们会忽视 BOM。改用 JSON 或导入时手动选 UTF-8。
何时不该用浏览器版
| 场景 | 用什么 |
|---|---|
| 文件 > 1 GB | DB Browser for SQLite(直接读磁盘) |
| 需要 SQLCipher 加密库 | sqlite3 CLI + PRAGMA key |
| 用户自定义 SQLite 扩展(FTS5 之外的) | 本地 SQLite + LOAD_EXTENSION |
| ATTACH 多个真实文件做联查 | 命令行 / DataGrip |
| 高频操作 / 持续编辑 | 桌面工具,避免每次都”下载 DB”覆盖原文件 |
浏览器版的甜区:500 MB 以内、临时排查、隐私敏感、跨平台分发——把链接发给同事就能看,不用让他们装东西。
体检清单
打开陌生 .db 后照着检查:
- 文件头是不是
SQLite format 3\0? - 表数量、行数总览是否符合预期?
- 有没有意料外的
_log_audit表? - 主键 / 索引是否齐全(看左侧”索引”区段)?
- 是否有触发器(CREATE TRIGGER)改写数据?
- 修改后下载的副本有没有被原应用接受(用 sqlite3 CLI 验证一次)?
浏览器查看 SQLite
把 .db .sqlite .sqlite3 .db3 文件直接拖进 SQLite 在线查看:左侧 schema 树 + SQL 编辑器(带表名列名补全 + 格式化 / 压缩按钮)+ 结果表(CSV/JSON/INSERT 一键导出)。文件不上传任何服务器,本地处理。