CSV key diff
Data conversion
Loading
Loading tool
The tool is loaded only when you open it.
All processing for this tool happens in your browser. Your input is not sent to a server.
About this tool
Compare two snapshots of a CSV table using the columns that identify each record, rather than its line number. Paste text or select local files, then choose a shared primary key or several columns as a composite key. The report separates added, removed, changed and unchanged rows, lists added and removed columns, and shows before/after cell differences. Order exports and warehouse inventory snapshots are useful examples: sorting a file differently should not look like hundreds of edits. Parsing and comparison run locally in a disposable browser worker; this tool does not upload or persist the input.
Common uses
- Reconcile old and new order exports by order_id to locate added orders, removed records and changed status or amount text.
- Compare inventory snapshots using warehouse plus sku so the same product at different locations remains a distinct record.
- Review a CSV schema change separately from row changes, then retain an exact-string JSON report or a long-form delta CSV for further review.
How to use it
- 1.Paste the before and after CSV text or choose a local file for each side. Set each side’s delimiter to comma, semicolon, tab or pipe and confirm whether its first row contains headers. Select the file encoding when needed; supported encodings are UTF-8, UTF-16LE and UTF-16BE. Run column inspection, then review the available columns and select one or more exact header names present in both tables. With headers disabled, use generated names such as column1 and column2. A composite key uses the selected values together, so each complete key must identify no more than one row per side.
- 2.Keep exact string comparison unless you intentionally enable trimming of leading/trailing whitespace or ignoring case. Both options apply to keys and cell equality, never to header names. Empty key components are rejected by default; allow them only if an empty value is meaningful and still unique. Run the comparison and review the row counts, added/removed columns and cell details. Resolve duplicate-key or parsing diagnostics before trying again; an ambiguous input does not produce a partial match. Cancel work if needed. Changes to the inputs or comparison settings invalidate the prior result.
- 3.Download the JSON report for original cell strings, or export delta CSV with kind,key,column,before,after,before_present,after_present columns. The key field is a JSON array. Keep formula protection enabled for spreadsheet use; choose raw export only after reviewing its warning and the destination application. Screen filters and shortened previews do not remove data from exports.
Reproducible keyed comparisons
Order status changes despite row reordering
{
"before": "order_id,status,total\n001,pending,12.50\n002,paid,0\n003,paid,7",
"after": "order_id,status,total\n003,paid,7\n001,paid,12.50\n004,pending,5",
"beforeOptions": {
"delimiter": ",",
"header": true
},
"afterOptions": {
"delimiter": ",",
"header": true
},
"compareOptions": {
"keys": [
"order_id"
],
"trim": false,
"ignoreCase": false,
"emptyKeys": "reject"
}
}{
"summary": {
"added": 1,
"removed": 1,
"changed": 1,
"unchanged": 1,
"cellsChanged": 1
},
"columnsAdded": [],
"columnsRemoved": []
}Use order_id as the key. Order 004 is added, 002 is removed, 001 changes status, and 003 is unchanged. Reordering the rows creates no extra differences; the output is a compact summary.
Inventory with a warehouse and SKU composite key
{
"before": "warehouse,sku,qty,note\nNorth,A,10,\"ready, packed\"\nSouth,A,5,\"line 1\nline 2\"\nNorth,B,8,hold",
"after": "sku,warehouse,note,qty\nA,South,\"line 1\nline 2\",6\nA,North,\"ready, packed\",10\nC,North,new,4",
"beforeOptions": {
"delimiter": ",",
"header": true
},
"afterOptions": {
"delimiter": ",",
"header": true
},
"compareOptions": {
"keys": [
"warehouse",
"sku"
],
"trim": false,
"ignoreCase": false,
"emptyKeys": "reject"
}
}{
"summary": {
"added": 1,
"removed": 1,
"changed": 1,
"unchanged": 1,
"cellsChanged": 1
},
"columnsAdded": [],
"columnsRemoved": []
}Select warehouse and sku together. South/A changes quantity from 5 to 6, North/B is removed, North/C is added, and North/A is unchanged. Reordered headers, the quoted comma and the quoted multiline note are parsed as data.
Match reordered columns by exact header name
{
"before": "id,name\n1,Alice",
"after": "name,id\nAlice,1",
"beforeOptions": {
"delimiter": ",",
"header": true
},
"afterOptions": {
"delimiter": ",",
"header": true
},
"compareOptions": {
"keys": [
"id"
],
"trim": false,
"ignoreCase": false,
"emptyKeys": "reject"
}
}{
"summary": {
"added": 0,
"removed": 0,
"changed": 0,
"unchanged": 1,
"cellsChanged": 0
},
"columnsAdded": [],
"columnsRemoved": []
}The name and id columns change position, but both names and values are the same. With headers enabled, this is one unchanged row.
Preserve leading zeros and decimal spelling
{
"before": "id,code,total\n001,007,1.0",
"after": "id,code,total\n001,7,1",
"beforeOptions": {
"delimiter": ",",
"header": true
},
"afterOptions": {
"delimiter": ",",
"header": true
},
"compareOptions": {
"keys": [
"id"
],
"trim": false,
"ignoreCase": false,
"emptyKeys": "reject"
}
}{
"summary": {
"added": 0,
"removed": 0,
"changed": 1,
"unchanged": 0,
"cellsChanged": 2
},
"columnsAdded": [],
"columnsRemoved": []
}All values are strings. For key 001, code changes from 007 to 7 and total changes from 1.0 to 1. The tool performs no numeric coercion or financial calculation.
Report column changes even when cells are empty
{
"before": "id,old\n1,",
"after": "id,new\n1,",
"beforeOptions": {
"delimiter": ",",
"header": true
},
"afterOptions": {
"delimiter": ",",
"header": true
},
"compareOptions": {
"keys": [
"id"
],
"trim": false,
"ignoreCase": false,
"emptyKeys": "reject"
}
}{
"summary": {
"added": 0,
"removed": 0,
"changed": 1,
"unchanged": 0,
"cellsChanged": 2
},
"columnsAdded": [
"new"
],
"columnsRemoved": [
"old"
]
}The old column is removed and new is added. The matched row is changed because an absent column differs from a present empty string. This is a structural difference, not a column rename inference.
Opt in to trimming and case-insensitive equality
{
"before": "id,status\n A , Paid ",
"after": "id,status\na,paid",
"beforeOptions": {
"delimiter": ",",
"header": true
},
"afterOptions": {
"delimiter": ",",
"header": true
},
"compareOptions": {
"keys": [
"id"
],
"trim": true,
"ignoreCase": true,
"emptyKeys": "reject"
}
}{
"summary": {
"added": 0,
"removed": 0,
"changed": 0,
"unchanged": 1,
"cellsChanged": 0
},
"columnsAdded": [],
"columnsRemoved": []
}With both options enabled, the key and status compare equal. The original spelling and surrounding whitespace remain in the JSON report. These options do not change header matching.
Reject ambiguous duplicate keys
{
"before": "id,name\n1,Alice\n1,Bob",
"after": "id,name\n1,Alice",
"beforeOptions": {
"delimiter": ",",
"header": true
},
"afterOptions": {
"delimiter": ",",
"header": true
},
"compareOptions": {
"keys": [
"id"
],
"trim": false,
"ignoreCase": false,
"emptyKeys": "reject"
}
}{
"error": "duplicateKey",
"side": "before"
}Both before rows have key 1. Comparison stops with a duplicate-key diagnostic instead of selecting Alice, Bob or a first/last row arbitrarily.
Compare headerless files with different delimiters
{
"before": "001;alpha\n002;beta",
"after": "001|alpha\n003|gamma",
"beforeOptions": {
"delimiter": ";",
"header": false
},
"afterOptions": {
"delimiter": "|",
"header": false
},
"compareOptions": {
"keys": [
"column1"
],
"trim": false,
"ignoreCase": false,
"emptyKeys": "reject"
}
}{
"summary": {
"added": 1,
"removed": 1,
"changed": 0,
"unchanged": 1,
"cellsChanged": 0
},
"columnsAdded": [],
"columnsRemoved": []
}Choose semicolon on the before side and pipe on the after side, with headers disabled for both. The generated column1 is the key; 002 is removed, 003 is added and 001 is unchanged.
Common CSV comparison mistakes
- Choosing only sku when each warehouse has a separate row for the same product.
- Leaving a wrong delimiter or header setting selected and then choosing keys from incorrectly parsed columns.
- Assuming 001 and 1, or 1.0 and 1, are equivalent despite string-only comparison.
- Enabling case or whitespace normalization without checking whether it merges previously distinct keys.
- Interpreting a changed identifier as an in-place edit, or expecting automatic column-rename detection.
- Opening raw delta CSV from an untrusted source in a spreadsheet, or sharing an export without checking its private contents.
Limits and notes
- Each side is limited to 2 MiB of file bytes and 2 MiB of UTF-8-encoded text after decoding, 10,000 data rows, 128 columns and 200,000 cells. An export is limited to 12 MiB. A disposable worker has a deadline and can be cancelled; exceeding a bound stops the operation. For larger files, split both snapshots along a key partition or use a local database/data-processing tool. A worker deadline is not a hard memory sandbox. Files must decode strictly as UTF-8, UTF-16LE or UTF-16BE. A BOM must agree with the selected encoding and is removed before parsing. BOM handling does not make legacy encodings such as GBK or Shift_JIS supported. Re-export such files as a supported encoding first. The selected delimiter is explicit; malformed quoting and inconsistent row widths are rejected rather than repaired. The screen shows 25 rows per page, at most 12 cells and 6 key components per row, with shortened long values. Use an export for the complete report.
- Headers match by exact name, independently of column order. Blank or duplicate headers are rejected. Case and whitespace options do not rename or normalize headers. Without a header row, generated column1, column2 and later names refer to positions, so rearranging headerless columns changes their meaning. Added or removed columns are structural differences. A matching row can therefore be changed even when the affected cell is empty. Column renames are not inferred, and a changed key appears as a removed row plus an added row. Row order itself is ignored.
- Duplicate complete keys on either side stop the comparison, including collisions introduced by optional trimming or case conversion. No first-row, last-row or many-to-many rule is guessed. An empty component in any chosen key is rejected by default; explicit allowance does not disable duplicate-key checks. Every cell remains a string: 001 differs from 1, and 1.0 differs from 1. Trimming removes only leading/trailing whitespace; ignoring case does not provide fuzzy matching, Unicode normalization, locale-aware collation or numeric tolerance. No dates, numbers or formulas are evaluated.
- JSON keeps original parsed strings even when equality options are enabled; it is not a byte-for-byte copy of the source file. Delta CSV is a long-form change report, not a merged table or import-ready patch. Safe export prefixes formula-like fields with an apostrophe, changing their exported text. Raw export can trigger spreadsheet formulas; CSV quoting alone is not protection, and spreadsheet applications may reinterpret data differently. The before_present and after_present fields distinguish an absent cell from an empty string. Saving and reopening a CSV in a spreadsheet may remove apostrophe protection; no CSV export is universally safe across all applications. Delta CSV emits one record per affected cell, including each present cell in an added or removed row; unchanged rows are retained only in JSON. JSON uses null for an absent cell, while the literal text null remains a string. Column-only changes in tables with no data rows produce no cell records in delta CSV; use JSON for the complete column metadata.
- Cell text is displayed as text, without running HTML, scripts or formulas. Inputs and results remain in page memory until cleared or the page is closed; the tool adds no upload or persistence. Downloaded reports may contain private data and remain outside the page’s control.
Frequently asked questions
How do I choose keys and resolve duplicate-key errors?
Choose columns that are present on both sides and identify one record uniquely. Use order_id for orders, or warehouse and sku together for per-location inventory. A unique row number generated after sorting is usually a poor key because it may refer to a different record in the next export. Multiple rows with the same selected key leave the intended match ambiguous. The diagnostic identifies the affected side and 1-based data-row numbers. Parsing errors use CSV record numbers including the header, which may differ from physical line numbers for multiline fields. Add a missing key column or deduplicate the source deliberately, then rerun. Trimming and ignoring case can create duplicates that were distinct under exact comparison.
Can delimiters, column orders and encodings differ?
Yes. Input settings are independent for each side. A comma-separated UTF-8 file can be compared with a supported UTF-16 tab-separated file. When headers are present, columns match by their exact names even if reordered. This does not translate, trim or infer renamed headers.
Does ignoring case or whitespace change my data?
It changes only equality and key matching. Original parsed strings remain in JSON before/after values. The options also apply to key cells, so review possible collisions. Headers continue to require exact equality.
Which export preserves original values and missing cells?
Use JSON when preserving original strings and structural distinctions matters. Delta CSV provides long-form changes with a JSON-array key. Keep its apostrophe-based formula protection enabled for spreadsheets. Raw CSV preserves cell text but may be interpreted as formulas; it is intended only for destinations you have assessed. An empty string is a value in an existing column. A missing column is a schema difference. The report preserves that distinction, lists added/removed columns, and includes affected cells in matching rows instead of silently treating them as equal.