CSV 关联合并
数据互转
正在载入
正在加载工具
工具代码会在打开时按需载入,请稍候。
本工具的全部运算都发生在你的浏览器中,输入内容不会发送到任何服务器。
关于这个工具
把客户姓名补充到订单导出表,或按地区关联查询表,无需上传文件。将左表键列逐项映射到右表键列,即使列名不同也能关联;随后选择左连接、内连接或全连接,并勾选需要保留的字段。输出列统一添加 left. 或 right. 前缀,避免同名列混淆。默认遇到重复键就停止。确需一对多或多对多关联时,必须显式选择展开,查看预检数量并确认笛卡尔积匹配。解析、预检和导出均在浏览器本地的一次性 Worker 中运行。
常见用途
- 将订单表的 customer_id 映射到客户表的 id,以左连接保留 order_id、total、name 和 tier。
- 以 region 与 customer_id 组成复合键关联地区数据,右表可以使用不同的键列名。
- 通过全连接检查已匹配与未匹配记录,下载所选数据列及左右来源行号以便核对。
使用方法
- 1.分别粘贴 CSV 或选择本地文件,并为每一侧指定分隔符、是否有表头及文件编码。支持逗号、分号、制表符和竖线分隔,以及 UTF-8、UTF-16LE、UTF-16BE 解码。检查列后,按顺序添加一个或多个左右键映射。列名按原样识别;无表头时使用 column1、column2 等生成列名。选择左连接、内连接或全连接,再选择两侧要输出的列。
- 2.默认按字符串精确匹配键,仅在确有需要时启用首尾空白裁剪或忽略大小写。选择空键分量应被拒绝、允许与另一个空值匹配,还是始终不匹配。运行预检,核对重复键组、匹配和未匹配的来源行数、预计输出行数、单元格数及序列化字节数。默认拒绝任何重复键,合法查询关系中的“多”侧也一样。需要保留重复键关系时,显式选择笛卡尔积展开,并在预检通过后确认本次展开,再执行合并。修改输入或选项会使原有结果与确认失效。
- 3.在分页结果中检查左右数据行号。左连接保留全部左表行,内连接仅保留匹配行对,全连接还会追加未匹配的右表行。下载 JSON 可保留原始字段字符串及代表缺失侧的 null。CSV 先输出五个来源元数据列,再输出带前缀的所选字段。用表格软件查看时保持公式防护开启;选择原始 CSV 前应确认其风险与目标软件。预览分页和文本缩略不会删减下载内容。
可执行的 CSV 关联示例
左连接为订单补充客户信息
{
"left": "order_id,customer_id,total\nA100,001,12.50\nA101,002,0\nA102,009,7",
"right": "id,name,tier\n001,Alice,gold\n002,Bob,silver\n003,Chika,bronze",
"leftOptions": {
"delimiter": ",",
"header": true
},
"rightOptions": {
"delimiter": ",",
"header": true
},
"joinOptions": {
"kind": "left",
"keys": [
{
"left": "customer_id",
"right": "id"
}
],
"leftColumns": [
"order_id",
"total"
],
"rightColumns": [
"name",
"tier"
],
"trim": false,
"ignoreCase": false,
"emptyKeys": "reject",
"duplicates": "reject"
}
}{
"headers": [
"left.order_id",
"left.total",
"right.name",
"right.tier"
],
"summary": {
"matched": 2,
"leftOnly": 1,
"rightOnly": 0,
"total": 3
},
"rows": [
{
"kind": "matched",
"leftRow": 1,
"rightRow": 1,
"values": [
"A100",
"12.50",
"Alice",
"gold"
]
},
{
"kind": "matched",
"leftRow": 2,
"rightRow": 2,
"values": [
"A101",
"0",
"Bob",
"silver"
]
},
{
"kind": "left-only",
"leftRow": 3,
"rightRow": null,
"values": [
"A102",
"7",
null,
null
]
}
]
}将 customer_id 映射到 id,仅保留所选订单与客户字段。A100、A101 匹配;A102 没有对应客户,因此右侧字段为 null。字符串 12.50 保持原样。
内连接只保留匹配订单
{
"left": "order_id,customer_id,total\nA100,001,12.50\nA101,002,0\nA102,009,7",
"right": "id,name,tier\n001,Alice,gold\n002,Bob,silver\n003,Chika,bronze",
"leftOptions": {
"delimiter": ",",
"header": true
},
"rightOptions": {
"delimiter": ",",
"header": true
},
"joinOptions": {
"kind": "inner",
"keys": [
{
"left": "customer_id",
"right": "id"
}
],
"leftColumns": [
"order_id",
"total"
],
"rightColumns": [
"name",
"tier"
],
"trim": false,
"ignoreCase": false,
"emptyKeys": "reject",
"duplicates": "reject"
}
}{
"headers": [
"left.order_id",
"left.total",
"right.name",
"right.tier"
],
"summary": {
"matched": 2,
"leftOnly": 0,
"rightOnly": 0,
"total": 2
},
"rows": [
{
"kind": "matched",
"leftRow": 1,
"rightRow": 1,
"values": [
"A100",
"12.50",
"Alice",
"gold"
]
},
{
"kind": "matched",
"leftRow": 2,
"rightRow": 2,
"values": [
"A101",
"0",
"Bob",
"silver"
]
}
]
}相同输入只产生 A100、A101。内连接既不保留未匹配订单 A102,也不保留没有订单的客户 Chika。
全连接保留未匹配订单和客户
{
"left": "order_id,customer_id,total\nA100,001,12.50\nA101,002,0\nA102,009,7",
"right": "id,name,tier\n001,Alice,gold\n002,Bob,silver\n003,Chika,bronze",
"leftOptions": {
"delimiter": ",",
"header": true
},
"rightOptions": {
"delimiter": ",",
"header": true
},
"joinOptions": {
"kind": "full",
"keys": [
{
"left": "customer_id",
"right": "id"
}
],
"leftColumns": [
"order_id",
"total"
],
"rightColumns": [
"name",
"tier"
],
"trim": false,
"ignoreCase": false,
"emptyKeys": "reject",
"duplicates": "reject"
}
}{
"headers": [
"left.order_id",
"left.total",
"right.name",
"right.tier"
],
"summary": {
"matched": 2,
"leftOnly": 1,
"rightOnly": 1,
"total": 4
},
"rows": [
{
"kind": "matched",
"leftRow": 1,
"rightRow": 1,
"values": [
"A100",
"12.50",
"Alice",
"gold"
]
},
{
"kind": "matched",
"leftRow": 2,
"rightRow": 2,
"values": [
"A101",
"0",
"Bob",
"silver"
]
},
{
"kind": "left-only",
"leftRow": 3,
"rightRow": null,
"values": [
"A102",
"7",
null,
null
]
},
{
"kind": "right-only",
"leftRow": null,
"rightRow": 3,
"values": [
null,
null,
"Chika",
"bronze"
]
}
]
}全连接保留 A102,并在所有左侧行之后追加 Chika。为 null 的来源行号和字段标明缺失侧,不会与空字符串混淆。
映射地区与客户编号复合键
{
"left": "region,customer_id,order_id\nNorth,007,A201\nSouth,007,A202",
"right": "territory,id,name\nSouth,007,Minami\nNorth,007,Kita",
"leftOptions": {
"delimiter": ",",
"header": true
},
"rightOptions": {
"delimiter": ",",
"header": true
},
"joinOptions": {
"kind": "left",
"keys": [
{
"left": "region",
"right": "territory"
},
{
"left": "customer_id",
"right": "id"
}
],
"leftColumns": [
"region",
"customer_id",
"order_id"
],
"rightColumns": [
"name"
],
"trim": false,
"ignoreCase": false,
"emptyKeys": "reject",
"duplicates": "reject"
}
}{
"headers": [
"left.region",
"left.customer_id",
"left.order_id",
"right.name"
],
"summary": {
"matched": 2,
"leftOnly": 0,
"rightOnly": 0,
"total": 2
},
"rows": [
{
"kind": "matched",
"leftRow": 1,
"rightRow": 2,
"values": [
"North",
"007",
"A201",
"Kita"
]
},
{
"kind": "matched",
"leftRow": 2,
"rightRow": 1,
"values": [
"South",
"007",
"A202",
"Minami"
]
}
]
}客户编号 007 在两个地区均出现。将 region 映射到 territory、customer_id 映射到 id 后,两个元组仍不同。右表行序不同不会改变结果的左侧顺序。
区分带前导零的键
{
"left": "customer_id,code\n001,007\n1,7",
"right": "id,name\n1,Alice",
"leftOptions": {
"delimiter": ",",
"header": true
},
"rightOptions": {
"delimiter": ",",
"header": true
},
"joinOptions": {
"kind": "left",
"keys": [
{
"left": "customer_id",
"right": "id"
}
],
"leftColumns": [
"customer_id",
"code"
],
"rightColumns": [
"id",
"name"
],
"trim": false,
"ignoreCase": false,
"emptyKeys": "reject",
"duplicates": "reject"
}
}{
"headers": [
"left.customer_id",
"left.code",
"right.id",
"right.name"
],
"summary": {
"matched": 1,
"leftOnly": 1,
"rightOnly": 0,
"total": 2
},
"rows": [
{
"kind": "left-only",
"leftRow": 1,
"rightRow": null,
"values": [
"001",
"007",
null,
null
]
},
{
"kind": "matched",
"leftRow": 2,
"rightRow": 1,
"values": [
"1",
"7",
"1",
"Alice"
]
}
]
}键 001 不匹配 1,不会通过数字转换移除前导零;所选 code 值 007 也按字符串原样保留。
只规范化键,不改写输出文本
{
"left": "customer_id,status\n A , Paid ",
"right": "id,name\na,ALICE",
"leftOptions": {
"delimiter": ",",
"header": true
},
"rightOptions": {
"delimiter": ",",
"header": true
},
"joinOptions": {
"kind": "left",
"keys": [
{
"left": "customer_id",
"right": "id"
}
],
"leftColumns": [
"customer_id",
"status"
],
"rightColumns": [
"id",
"name"
],
"trim": true,
"ignoreCase": true,
"emptyKeys": "reject",
"duplicates": "reject"
}
}{
"headers": [
"left.customer_id",
"left.status",
"right.id",
"right.name"
],
"summary": {
"matched": 1,
"leftOnly": 0,
"rightOnly": 0,
"total": 1
},
"rows": [
{
"kind": "matched",
"leftRow": 1,
"rightRow": 1,
"values": [
" A ",
" Paid ",
"a",
"ALICE"
]
}
]
}空白裁剪和忽略大小写使 A 与 a 匹配,但原键 " A "、状态 " Paid " 和名称 "ALICE" 都不会在结果中被改写。
默认拒绝重复客户键
{
"left": "customer_id,order_id\n001,A1\n001,A2",
"right": "id,name\n001,Alice",
"leftOptions": {
"delimiter": ",",
"header": true
},
"rightOptions": {
"delimiter": ",",
"header": true
},
"joinOptions": {
"kind": "left",
"keys": [
{
"left": "customer_id",
"right": "id"
}
],
"leftColumns": [
"order_id"
],
"rightColumns": [
"name"
],
"trim": false,
"ignoreCase": false,
"emptyKeys": "reject",
"duplicates": "reject"
}
}{
"error": "duplicateKey"
}左表有两笔订单共享客户键 001。即使右侧查询键唯一,默认仍拒绝重复关联键;若要保留两笔订单,应明确选择展开。
显式展开二乘二关系
{
"left": "customer_id,order_id\n001,A1\n001,A2",
"right": "id,contact\n001,home\n001,work",
"leftOptions": {
"delimiter": ",",
"header": true
},
"rightOptions": {
"delimiter": ",",
"header": true
},
"joinOptions": {
"kind": "left",
"keys": [
{
"left": "customer_id",
"right": "id"
}
],
"leftColumns": [
"order_id"
],
"rightColumns": [
"contact"
],
"trim": false,
"ignoreCase": false,
"emptyKeys": "reject",
"duplicates": "expand"
},
"approveExpansion": true
}{
"headers": [
"left.order_id",
"right.contact"
],
"summary": {
"matched": 4,
"leftOnly": 0,
"rightOnly": 0,
"total": 4
},
"rows": [
{
"kind": "matched",
"leftRow": 1,
"rightRow": 1,
"values": [
"A1",
"home"
]
},
{
"kind": "matched",
"leftRow": 1,
"rightRow": 2,
"values": [
"A1",
"work"
]
},
{
"kind": "matched",
"leftRow": 2,
"rightRow": 1,
"values": [
"A2",
"home"
]
},
{
"kind": "matched",
"leftRow": 2,
"rightRow": 2,
"values": [
"A2",
"work"
]
}
]
}将 duplicates 设为 expand 并明确批准通过的预检后,两笔订单和两条联系记录组成四个行对。先按左表订单顺序,再保留各订单内部的右表联系记录顺序。
区分缺失、空字符串和文本 null
{
"left": "customer_id,order_id\n1,A1\n2,A2\n3,A3",
"right": "id,note\n1,\n3,null",
"leftOptions": {
"delimiter": ",",
"header": true
},
"rightOptions": {
"delimiter": ",",
"header": true
},
"joinOptions": {
"kind": "left",
"keys": [
{
"left": "customer_id",
"right": "id"
}
],
"leftColumns": [
"order_id"
],
"rightColumns": [
"note"
],
"trim": false,
"ignoreCase": false,
"emptyKeys": "reject",
"duplicates": "reject"
}
}{
"headers": [
"left.order_id",
"right.note"
],
"summary": {
"matched": 2,
"leftOnly": 1,
"rightOnly": 0,
"total": 3
},
"rows": [
{
"kind": "matched",
"leftRow": 1,
"rightRow": 1,
"values": [
"A1",
""
]
},
{
"kind": "left-only",
"leftRow": 2,
"rightRow": null,
"values": [
"A2",
null
]
},
{
"kind": "matched",
"leftRow": 3,
"rightRow": 2,
"values": [
"A3",
"null"
]
}
]
}A1 对应已有空备注,A2 没有匹配客户,A3 的备注为四个字符的文本 null。JSON 分别保存为 ""、null 和 "null",并记录来源行号。
关联不同分隔符的无表头数据
{
"left": "001;A1\n002;A2",
"right": "001|Alice\n003|Chika",
"leftOptions": {
"delimiter": ";",
"header": false
},
"rightOptions": {
"delimiter": "|",
"header": false
},
"joinOptions": {
"kind": "full",
"keys": [
{
"left": "column1",
"right": "column1"
}
],
"leftColumns": [
"column1",
"column2"
],
"rightColumns": [
"column2"
],
"trim": false,
"ignoreCase": false,
"emptyKeys": "reject",
"duplicates": "reject"
}
}{
"headers": [
"left.column1",
"left.column2",
"right.column2"
],
"summary": {
"matched": 1,
"leftOnly": 1,
"rightOnly": 1,
"total": 3
},
"rows": [
{
"kind": "matched",
"leftRow": 1,
"rightRow": 1,
"values": [
"001",
"A1",
"Alice"
]
},
{
"kind": "left-only",
"leftRow": 2,
"rightRow": null,
"values": [
"002",
"A2",
null
]
},
{
"kind": "right-only",
"leftRow": null,
"rightRow": 2,
"values": [
null,
null,
"Chika"
]
}
]
}左侧使用分号、右侧使用竖线,两侧均无表头。映射生成的 column1,再输出所选位置列。前缀用于区分数据来源。
空键始终不匹配
{
"left": "customer_id,order_id\n,A1",
"right": "id,name\n,Alice",
"leftOptions": {
"delimiter": ",",
"header": true
},
"rightOptions": {
"delimiter": ",",
"header": true
},
"joinOptions": {
"kind": "full",
"keys": [
{
"left": "customer_id",
"right": "id"
}
],
"leftColumns": [
"order_id"
],
"rightColumns": [
"name"
],
"trim": false,
"ignoreCase": false,
"emptyKeys": "never-match",
"duplicates": "reject"
}
}{
"headers": [
"left.order_id",
"right.name"
],
"summary": {
"matched": 0,
"leftOnly": 1,
"rightOnly": 1,
"total": 2
},
"rows": [
{
"kind": "left-only",
"leftRow": 1,
"rightRow": null,
"values": [
"A1",
null
]
},
{
"kind": "right-only",
"leftRow": null,
"rightRow": 1,
"values": [
null,
"Alice"
]
}
]
}选择 never-match 后,即使两侧键字段都为空也不匹配。全连接因此输出一个 left-only 行和一个 right-only 行。
显式允许空键匹配
{
"left": "customer_id,order_id\n,A1",
"right": "id,name\n,Alice",
"leftOptions": {
"delimiter": ",",
"header": true
},
"rightOptions": {
"delimiter": ",",
"header": true
},
"joinOptions": {
"kind": "left",
"keys": [
{
"left": "customer_id",
"right": "id"
}
],
"leftColumns": [
"order_id"
],
"rightColumns": [
"name"
],
"trim": false,
"ignoreCase": false,
"emptyKeys": "allow",
"duplicates": "reject"
}
}{
"headers": [
"left.order_id",
"right.name"
],
"summary": {
"matched": 1,
"leftOnly": 0,
"rightOnly": 0,
"total": 1
},
"rows": [
{
"kind": "matched",
"leftRow": 1,
"rightRow": 1,
"values": [
"A1",
"Alice"
]
}
]
}选择 allow 后,两个空字符串键相等。该选项不取消重复键检查或展开上限,而是显式改变空键的匹配含义。
CSV 关联时的常见错误
- 客户编号只在各地区内唯一,却仅选 customer_id;应补充 region 到 territory 的映射。
- 需要保留没有客户资料的订单,却选择了内连接。
- 以为重复键只会取一条查询结果,或未检查 m × n 行数就开启展开。
- 开启空白裁剪或忽略大小写后,没有检查原本不同的键是否变成相同。
- 将 001 与 1 当作相同编号,或认为 CSV 能强制表格软件保留前导零。
- 删掉来源元数据列后,把缺失侧的空白误认为已有空字段。
- 在表格软件中打开不可信来源的原始 CSV,或未检查内容就分享下载报告。
限制与说明
- 每个输入最多 2 MiB 文件字节、解码后 2 MiB UTF-8 文本、10,000 个数据行、128 列和 200,000 个数据单元格。输出最多 50,000 行、500,000 个单元格(包含每行五个 CSV 来源列),序列化 JSON 与带防护 CSV 各不超过 12 MiB。预检会在构造合并结果行之前检查行数增长与序列化大小,超限直接阻止合并,不会截断。Worker 可取消且有运行期限,但并非严格的浏览器内存沙箱。更大的文件应按相同键分区拆分,或改用本地数据库。
- 所有值都保留为字符串:001 与 1 是不同的键,1.0 不会变成 1。可选的首尾空白裁剪与转小写仅作用于关联键,不改变输出值或表头,也不提供模糊匹配、数值或日期转换、Unicode 规范化或区域化排序。复合键按元组识别,不以可能歧义的分隔符拼接。任一规范化后的键分量为空时按所选空键策略处理。重复检测使用同样的规范化键,因此开启选项可能产生冲突。“始终不匹配”下的空键各自保持未匹配,不会组成共同的空键组。
- 任一侧的完整键重复时,默认阻止关联,未匹配的重复键组也不例外,不会擅自选第一行或最后一行。显式展开会为相同键生成全部行对:左侧两行与右侧三行产生六行。左侧多行匹配右侧一行也需要该策略和确认。统计对应所选连接方式,其中 matched 是输出匹配行对数,可能大于匹配的来源行数。结果先按左表原顺序排列,每个左行的右侧匹配按右表顺序排列,全连接最后追加未匹配右行。不执行聚合、去重、模糊关联或 SQL。
- 每个所选输出列都命名为 left.<原始列名> 或 right.<原始列名>;两侧同名列或键列均被选中时,两份都会保留。JSON 对缺失侧的字段使用 null,已有空单元格仍是 "",文本 null 仍是 "null";每行还包含类型和从 1 开始、可为 null 的 leftRow/rightRow 来源行号。CSV 将缺失值写为空白,并通过 _join_kind、_left_row、_right_row、_left_present、_right_present 区分来源是否存在。CSV 公式防护会给风险字段添加单引号,因此会改变导出的字段文本。原始 CSV 可能触发表格公式,表格软件还可能改写编号或在保存后重新打开时移除防护。精确字符串交换建议使用 JSON;它不保留原文件的引号形式或换行字节。
- 文件必须严格按所选支持编码解码,BOM 必须与编码一致。GBK、Shift_JIS 等需预先转换。错误引号、行宽不一致、空表头和完全重复的表头会被拒绝,表头不会自动裁剪或重命名。支持带引号的分隔符、双写引号及多行字段。来源行诊断从第 1 个数据行计数;解析诊断按包含表头的 CSV 记录计数,不等于带引号文本中的物理行号。工具不上传或持久化输入;单元格按文本显示。下载文件可能含有私密信息,且不受页面继续控制。预览每页显示 25 行及所选数据列中的前 12 列,并缩略过长表头与值;导出会保留全部所选字段。
常见问题
订单和客户表应该选哪种连接?
将订单放左侧,把 customer_id 映射到右表 id。左连接保留缺少客户的订单,其 JSON 客户字段为 null;内连接会去掉这些未匹配订单;全连接还会列出没有订单的客户。若多笔订单共享 customer_id,默认重复键错误是预期行为:请显式选择展开;如果需要多对一查询,应确认客户侧键唯一,再核对并确认预计结果。
不同列名或复合键如何映射?
为每个键分量添加一组对应关系,例如 left.region 对 right.territory,再添加 left.customer_id 对 right.id。匹配按映射顺序比较整个元组。左右列名不必相同,但每个名称都必须精确对应各自表中的现有列。前导零有意义。需要检查键的原样文本时,可把两侧键列都选为输出列。
为什么展开前还需要预检和确认?
重复键可能使结果数量意外增长。同一键在左表出现 m 次、右表出现 n 次时,展开产生 m × n 个匹配行对。预检在构造结果行前展示重复情况、来源行统计及此处理范围内准确的预计序列化大小。通过检查并不代表工具替你决定了业务关系。确认只适用于当前输入和选项;修改后必须重新预检和确认。
能区分未匹配字段、空字符串和文本 null 吗?
可以。JSON 中缺失侧字段为 null,已有空字段为 "",文本 null 为 "null";每行还带有类型和来源行号。CSV 本身没有 null 类型,因此工具在写入空白缺失字段的同时输出 _left_present、_right_present 标记及来源行号。检查 CSV 时请保留这些元数据列。带防护 CSV 可能改变类似公式的字符串,重视精确解析文本时应选择 JSON。