浏览器本地 SQL 工作台
数据互转
正在载入
正在加载工具
工具代码会在打开时按需载入,请稍候。
本工具的全部运算都发生在你的浏览器中,输入内容不会发送到任何服务器。
关于这个工具
把 CSV 或扁平 JSON 数组导入具名临时表,再用真实的 SQLite SQL 筛选、关联、分组和排序。CSV 单元格保持文本,包括前导零;JSON 基本值保留支持的数值、文本和 NULL 类型。sql.js 引擎在本地 Web Worker 中执行,工具不会上传输入、访问数据中的网址或保存输入历史。随站点打包的同源 SQLite WASM 资源为 658,410 字节(约 643 KiB),另有 Worker JavaScript;只有明确执行载入操作后才会获取。表只存在于当前页面的临时会话中。本工作台接收 CSV/JSON 表并执行只读查询;打开 SQLite 数据库文件或 Parquet 文件不在本工具范围内。
常见用途
- 把订单 CSV 与客户 JSON 对照表关联,用 LEFT JOIN 保留没有匹配客户的订单。
- 筛选导出数据,明确转换数字文本,按部门或地区聚合,并下载核对后的结果。
- 检查 JSON 缺失值、比较字符串标识符,或用 WITH 公共表表达式编写清晰的多步查询。
使用方法
- 1.选择 CSV 或 JSON,输入唯一表名,粘贴文本或选择本地文件并指定 UTF-8、UTF-16LE 或 UTF-16BE。CSV 需明确选择逗号、分号、制表符或竖线及是否含表头。添加最多 8 个来源草稿,批量载入后查看每张表的列名和行数。任一来源无效都会阻止整批导入,不会悄悄跳过。JSON 必须是扁平对象数组;嵌套数据应先单独转换。
- 2.使用显示的表名和列名编写一条 SELECT 查询,可由 WITH 开头。含空格或标点的标识符使用双引号。CSV 中的数字是文本:先确认其为有效数字,再使用 CAST。添加 ORDER BY 获得可重现的顺序,用 WHERE 或 LIMIT 将结果限制在声明的范围内,然后运行。
- 3.下载 CSV 前核对结果行数和分页预览。公式防护默认开启;只有读过风险提示后才明确选择原始 CSV。导出包含成功结果的全部有界数据,而不只是当前页。取消或重置会释放 Worker 并丢弃所有已导入的表,继续使用时需重新导入来源。
可执行的 CSV 与 JSON SQL 示例
筛选数字文本并保留编号
{
"sources": [
{
"name": "stock",
"format": "csv",
"text": "id,units\n001,12\n002,3\n010,20",
"delimiter": ",",
"header": true
}
],
"sql": "SELECT id, units FROM stock WHERE CAST(units AS INTEGER) >= 10 ORDER BY CAST(units AS INTEGER) DESC",
"safe": true
}{
"columns": [
"id",
"units"
],
"rows": [
[
"010",
"20"
],
[
"001",
"12"
]
]
}CAST 改变比较方式,不改变选出的 id 或 units 字符串。返回编号仍是 010 和 001;显式 ORDER BY 将 20 排在 12 前面。
导入 JSON 布尔值、数字和缺失键
{
"sources": [
{
"name": "tickets",
"format": "json",
"text": "[{\"id\":\"T1\",\"open\":true,\"score\":4.5},{\"id\":\"T2\",\"open\":false,\"score\":null},{\"id\":\"T3\",\"open\":true}]",
"delimiter": ",",
"header": true
}
],
"sql": "SELECT id, open, score FROM tickets ORDER BY id",
"safe": true
}{
"columns": [
"id",
"open",
"score"
],
"rows": [
[
"T1",
1,
4.5
],
[
"T2",
0,
null
],
[
"T3",
1,
null
]
]
}JSON true/false 变为 1/0,4.5 保持数值,明确的 null 与缺失的 score 键都得到 SQL NULL。编号字符串不变。
将 CSV 左连接到 JSON 对照表
{
"sources": [
{
"name": "orders",
"format": "csv",
"text": "order_id,customer_id,total\nA100,001,12.50\nA101,002,0\nA102,009,7",
"delimiter": ",",
"header": true
},
{
"name": "customers",
"format": "json",
"text": "[{\"id\":\"001\",\"name\":\"Alice\"},{\"id\":\"002\",\"name\":\"ボブ\"}]",
"delimiter": ",",
"header": true
}
],
"sql": "SELECT o.order_id, c.name, o.total FROM orders AS o LEFT JOIN customers AS c ON o.customer_id = c.id ORDER BY o.order_id",
"safe": true
}{
"columns": [
"order_id",
"name",
"total"
],
"rows": [
[
"A100",
"Alice",
"12.50"
],
[
"A101",
"ボブ",
"0"
],
[
"A102",
null,
"7"
]
]
}两个来源的客户编号均为字符串。未匹配的订单 A102 仍在结果中,客户名为 NULL,原始 total 仍是文本。
对已校验的整数金额分组求和
{
"sources": [
{
"name": "sales",
"format": "csv",
"text": "region,amount\nEast,10\nWest,7\nEast,20",
"delimiter": ",",
"header": true
}
],
"sql": "SELECT region, SUM(CAST(amount AS INTEGER)) AS total, COUNT(*) AS records FROM sales GROUP BY region ORDER BY region",
"safe": true
}{
"columns": [
"region",
"total",
"records"
],
"rows": [
[
"East",
30,
2
],
[
"West",
7,
1
]
]
}SUM 前明确将 amount 转为整数。East 两条记录合计 30,West 一条合计 7。此例使用整数,不宣称精确十进制运算。
用 WITH 查询筛选聚合结果
{
"sources": [
{
"name": "sales",
"format": "csv",
"text": "region,amount\nEast,10\nWest,7\nEast,20",
"delimiter": ",",
"header": true
}
],
"sql": "WITH totals AS (SELECT region, SUM(CAST(amount AS INTEGER)) AS total FROM sales GROUP BY region) SELECT region, total FROM totals WHERE total >= 10 ORDER BY region",
"safe": true
}{
"columns": [
"region",
"total"
],
"rows": [
[
"East",
30
]
]
}公共表表达式先计算各地区合计,外层 SELECT 只保留不少于 10 的合计。WITH 属于同一条只读语句。
按数值排序,同时保留原始写法
{
"sources": [
{
"name": "items",
"format": "csv",
"text": "id,quantity\nA,10\nB,2\nC,001",
"delimiter": ",",
"header": true
}
],
"sql": "SELECT id, quantity, CAST(quantity AS INTEGER) AS numeric_quantity FROM items ORDER BY CAST(quantity AS INTEGER), id",
"safe": true
}{
"columns": [
"id",
"quantity",
"numeric_quantity"
],
"rows": [
[
"C",
"001",
1
],
[
"B",
"2",
2
],
[
"A",
"10",
10
]
]
}CAST 提供数值排序与数值输出列。原始数量 001 仍保留为 001,旁边显示它的数值解释 1。
引用含空格和 Unicode 的标识符
{
"sources": [
{
"name": "商品",
"format": "csv",
"text": "名称,unit price\n猫,12.50",
"delimiter": ",",
"header": true
}
],
"sql": "SELECT \"名称\", \"unit price\" AS price FROM \"商品\"",
"safe": true
}{
"columns": [
"名称",
"price"
],
"rows": [
[
"猫",
"12.50"
]
]
}双引号引用表 商品 和列 unit price。值 猫 与价格字符串 12.50 原样返回;单引号则会创建字符串常量。
查询带引号的多行 CSV 字段
{
"sources": [
{
"name": "notes",
"format": "csv",
"text": "id,note\n001,\"first\nsecond\"\n002,\"comma, quote \"\"ok\"\"\"",
"delimiter": ",",
"header": true
}
],
"sql": "SELECT id, note FROM notes ORDER BY id",
"safe": true
}{
"columns": [
"id",
"note"
],
"rows": [
[
"001",
"first\nsecond"
],
[
"002",
"comma, quote \"ok\""
]
]
}解析遵循 CSV 记录边界:第一条备注内的换行仍属于一个值,成对双引号被还原为字面引号。
分别统计 NULL 与空字符串
{
"sources": [
{
"name": "entries",
"format": "json",
"text": "[{\"id\":\"A\",\"value\":\"\"},{\"id\":\"B\",\"value\":null},{\"id\":\"C\"}]",
"delimiter": ",",
"header": true
}
],
"sql": "SELECT COUNT(*) AS records, COUNT(value) AS non_null, SUM(value IS NULL) AS nulls FROM entries",
"safe": true
}{
"columns": [
"records",
"non_null",
"nulls"
],
"rows": [
[
3,
1,
2
]
]
}共三行,但只有一个非 NULL 值,即空字符串。明确的 JSON null 和缺失键都满足 IS NULL。
使用无表头 CSV 的生成列名
{
"sources": [
{
"name": "people",
"format": "csv",
"text": "001;Paris\n002;東京",
"delimiter": ";",
"header": false
}
],
"sql": "SELECT column1 AS id, column2 AS city FROM people ORDER BY column1",
"safe": true
}{
"columns": [
"id",
"city"
],
"rows": [
[
"001",
"Paris"
],
[
"002",
"東京"
]
]
}关闭表头并选择分号后,两行都属于数据。导入器生成 column1 和 column2,SELECT 别名为结果提供有意义的表头。
默认保护疑似公式的表头和值
{
"sources": [
{
"name": "sheet",
"format": "csv",
"text": "note,total\n=1+1,-42\n@SUM(A1),0",
"delimiter": ",",
"header": true
}
],
"sql": "SELECT note AS \"=header\", total FROM sheet ORDER BY total",
"safe": true
}{
"columns": [
"=header",
"total"
],
"rows": [
[
"=1+1",
"-42"
],
[
"@SUM(A1)",
"0"
]
],
"csvRecords": [
[
"'=header",
"total"
],
[
"'=1+1",
"'-42"
],
[
"'@SUM(A1)",
"0"
]
]
}查询结果行不变。csvRecords 展示导出防护如何给 =header 列名、疑似公式文本及 -42 添加单引号。普通 CSV 引号并不能阻止公式。
仅为可信目标明确选择原始 CSV
{
"sources": [
{
"name": "sheet",
"format": "csv",
"text": "note,total\n=1+1,-42",
"delimiter": ",",
"header": true
}
],
"sql": "SELECT note AS \"=header\", total FROM sheet",
"safe": false
}{
"columns": [
"=header",
"total"
],
"rows": [
[
"=1+1",
"-42"
]
],
"csvRecords": [
[
"=header",
"total"
],
[
"=1+1",
"-42"
]
]
}safe 为 false 时,csvRecords 与查询结果及表头完全一致。这种输出可能在电子表格中执行公式,因此选择原始导出前必须主动核对。
本地 SQL 常见错误
- 把 CSV 数字文本当成已经是数值类型的列进行比较或排序。
- 用 CAST 代替校验,意外把错误数字文本转换为零。
- 使用 = NULL 而不是 IS NULL,或把 COUNT(column) 当成总行数。
- 关联非唯一键,导致输出行数意外倍增。
- 给列名加单引号,而不是标识符所需的双引号。
- 导入嵌套 JSON、重复键,或没有加引号且超出 JavaScript 安全范围的整数。
- 以为取消 Worker 后导入表仍会保留,或过大查询失败后仍能导出部分结果。
- 在电子表格中打开不可信原始 CSV,或误以为 CSV 引号能阻止公式。
限制与说明
- 每个来源最多 2 MiB 原始文件字节和 2 MiB 解码后的 UTF-8 文本、10,000 数据行、128 列及 200,000 数据单元格。会话最多保留 8 张表,合计 8 MiB 原始来源字节和 8 MiB 解码后的 UTF-8 文本、50,000 行和 500,000 单元格。表名与列名不得为空,且最多 128 UTF-8 字节;仅 ASCII 大小写不同的名称冲突,以 sqlite_ 开头的表名保留。SQLite 原生分配上限为 64 MiB,数据库页上限为 32 MiB。解析后的输入、JavaScript 副本和 Worker 消息还会占用额外内存;这些并非浏览器总堆内存上限。
- 文件需明确选择 UTF-8、UTF-16LE 或 UTF-16BE 编码;错误字节和冲突的 BOM 会被拒绝。CSV 使用明确的分隔符。不等宽记录、错误引号、空白或重复表头会导致导入失败;不自动识别分隔符或传统编码。无表头列命名为 column1、column2 等。CSV 值始终为 TEXT,包括空字符串和看似数字的编号。为防止值被悄悄改写,导入文本中的 NUL 字符和未配对的 UTF-16 代理项会被拒绝。JSON 接受扁平对象数组,按首次出现顺序合并键,将缺失值和 JSON null 映射为 SQL NULL,布尔值映射为 1/0。嵌套值、重复对象键、不安全整数及不支持的有精度损失数字记法会被拒绝;精确编号和小数应以带引号的 JSON 字符串提供。
- 查询使用 SQLite 语法,必须是单条 SELECT 或 WITH…SELECT,最多 20,000 UTF-8 字节。不支持写入、PRAGMA、ATTACH、SQL 扩展、文件或网络访问、绑定参数及多条语句。结果最多 1,000 行、128 列、50,000 单元格、合计 2 MiB,单元格最多 256 KiB。超过任一结果限制会使查询失败,不保留部分结果或导出;请收紧 WHERE、减少选择的列或添加 LIMIT。预览每页 25 行,最多显示 12 列,每个单元格 500 字符;成功导出保留完整的有界结果。
- 类型转换与比较遵循 SQLite 规则。CAST 可能把无效数字文本变成零,超出有符号 64 位范围的 INTEGER 转换会饱和为边界值,因此计算前应校验数据。小数运算使用二进制浮点,并非精确十进制记账。超出 JavaScript 安全整数范围的 64 位整数查询结果以精确十进制字符串表示;BLOB 结果显示并导出为大写 X'hex' 文本,而非原始字节。没有 ORDER BY 时不保证行序。重复关联键可能使结果行数成倍增加;LIMIT 限制返回行数,但不能保证查询成本很低。
- CSV 输出最多 6 MiB,使用 UTF-8、逗号、带引号字段和 CRLF 记录分隔符,下载名为 sql-result.csv。SQL NULL 写为空字段,与空字符串的区别会丢失。公式防护会给风险表头和值添加单引号前缀,包括负数,并有意改变这些导出值。它无法阻止所有电子表格解释行为;原始 CSV 可能激活公式或转换编号。每次载入、查询、翻页或导出限时 10 秒。取消、超时、Worker 故障、修改来源、重置或离开工具都会丢弃内存中的所有表与结果;下载文件仍留在设备上且可能含私密信息。本工具不会创建数据库文件、外部扩展、远程表或保存的查询历史。
常见问题
为什么 CSV 的 ORDER BY 把 10 排在 2 前面?
CSV 列按文本导入,以保留 001 这类编号的原始写法。文本排序逐字符比较。确认数据是有效数字后,可用 ORDER BY CAST(quantity AS INTEGER) 或 CAST(amount AS REAL)。若原始字符串重要,请在 SELECT 中保留原列。CAST 不是校验:无效数字文本可能悄悄变成零。
空 CSV 字段和 JSON null 有何区别?
空 CSV 字段是空文本字符串。JSON null 与对象中缺失的键都映射为 SQL NULL;true 与 false 映射为 1 和 0。应使用 IS NULL,而非 = NULL。COUNT(column) 跳过 NULL,COUNT(*) 则计算行数。CSV 导出把 NULL 和空字符串都写为空字段,因此下载后无法区分两者。
能修改表、打开数据库文件或使用 SQL 参数吗?
导入控件会建立本地临时表,提交的 SQL 仅限单条只读查询。在资源限制内支持 SELECT、关联、聚合及 WITH…SELECT。INSERT、UPDATE、DELETE、CREATE、DROP、PRAGMA、ATTACH、绑定参数与扩展加载均被拒绝。本工具不打开 SQLite 数据库文件或 Parquet 文件;SQLite SQL 也不与所有其他数据库方言完全互通。
取消操作或查询结果过大时会怎样?
取消会终止 Worker,释放整个临时数据库及所有导入表,而不仅仅停止当前查询。重置或离开工具也会丢弃会话;继续使用需重新导入。超过行、列、单元格或字节限制的结果直接失败,不提供部分下载。请增加筛选、减少选择列或明确添加 LIMIT。仅预览被裁剪时,不会缩短成功的导出。