Apex Automation free tools
Merge Two CSV Files
Join them on a key, or stack them, and find out what did not match
Two files, one key column, and a number at the end that says how much of it worked. The joining is the easy part. The part that costs an afternoon is the fourteen rows that did not match, because one file wrote the id as 00721 and the other wrote it as 721. This page lists those rows, names the closest key on the other side, and tells you which single setting would match them.
- Free to use
- No account needed
- Nothing uploaded
- No row limit
The first file
Drop a .csv or .xlsx here, or
This is the list you want to keep. Every row of it survives a left join.
The second file
Drop a .csv or .xlsx here, or
This is the one you are looking things up in.
More files to stack
Drop any number here, or
Each one keeps its name in a source column, so you can tell afterwards where a row came from.
What these files can be
- .csv, .tsv or .xlsxSemicolon, tab and pipe separated too. In a workbook the first sheet holding a table is read, so a cover page with a title on it is stepped over.
- Key columns with different namesaccount_id on one side can meet customer on the other. Nothing has to be renamed first.
- Keys made of more than one columnFirst name plus postcode, or branch plus date. The parts are kept apart, so one column cannot bleed into the next.
- Any encodingUTF-8 and UTF-16, with or without a byte order mark. A CSV that Excel saved on Windows reads correctly.
- Any number of rowsNo cap and no sign up. The work happens on your own machine, so the limit is your machine.
- Columns you do not wantPick which columns come across from the second file. A column name that exists on both sides is renamed rather than quietly covering the other.
- Keys that do not quite agreeLeading zeros, upper and lower case, punctuation, accents, scientific notation, and company suffixes such as Inc and Ltd, each a separate switch that touches only the key.
- Keys that disagree more than thatFuzzy matching with a score on every match. Where two candidates are equally close, nothing is chosen and the row is named.
What will not work. A file with no header row is read with its first row as the header, which is the usual convention but worth knowing. A row whose key cell is empty never matches another empty key, because empty is not a value. And a join is not a merge of cell contents: where both files have a column of the same name, both are kept, side by side, under different names.
Working
Two different jobs, one word
Search for "merge CSV" and you will be shown two kinds of tool without being told which is which. One stacks files: more rows, the same columns, which is what you want when an export was split across months. The other joins them: the same rows, more columns, which is what VLOOKUP does and what people usually mean when they have a customer list and an orders list.
Almost everything ranking for that phrase does the first and calls it merging. The page you are on does both, with a switch at the top, because the words are not going to stop being confused and guessing which one somebody meant is not a service.
The fourteen rows that did not match
Every join tool prints a match rate. Almost none of them answers the question that follows it, which is the only question anybody actually has: which rows, and why.
The reasons are boringly consistent. One file has a trailing space. One was exported in capitals. An id went through a spreadsheet and 00721 came out as 721. A long account number came back as 1.23457E+11. A company is Acme Inc on one side and Acme, Inc. on the other. None of these is a data problem, and all of them look like one.
So the page does the obvious thing that nobody does: it tries.
Each of those settings is switched on behind the scenes, the join is run again, and the difference is counted. What comes back is a count rather than a suggestion: "turning on leading zeros would match 14 more rows". The same is done for fuzzy matching at five different thresholds, so the slider has a real number under it rather than a feeling. And for the rows still stuck, the page names the closest key on the other side with a similarity score, so you can see whether the record is genuinely missing or merely spelled differently.
What is claimed, and what is not. No accuracy percentage, because a join is exactly as accurate as its keys. What is claimed is checkable on your own file: every row that went in is either matched or listed as unmatched, the number of rows coming out is the number the keys say it should be, and every fuzzy match is shown with its score rather than folded in quietly.
Why a join can hand back more rows than it was given
Three hundred customers joined to three hundred orders can produce three hundred and forty rows, and the first reaction is always that something is broken. Nothing is. If a key appears twice on the other side, every matching row comes out twice, which is what a join means in every database there has ever been.
It is still worth knowing before it happens rather than after, so repeated keys are counted on both sides and the row count is worked out before the join runs. The worst offending key is named, with how many times it appears, because that is usually enough to see what went wrong in the export.
What has actually been tested
Every figure here is a test run that has to pass before anything reaches this page. If one fails, nothing ships. A number on a page that nothing checks goes stale, so a test reads this page, pulls out every figure in it, and compares each one with the code behind it.
- 32Every file that ships, scanned for generated copy, inline styles, off-origin loads and broken asset paths ship
- 20The live page in a real browser, under the security policy it ships with e2e
- 18Every join type, composite keys, blank keys and renamed columns, each row counted in and out join
- 17Layout at seven screen sizes, with the browser text enlarged, and the contrast of every line visual
- 11No cap: a hundred thousand rows each side, twenty files stacked, and the cost per row held flat limits
- 9Every control clicked in a real browser, to prove none of them is decoration ui
- 12The settings that only touch the key, and the column profiling behind the suggestion keys
- 10Fuzzy matching, its scores, its refusals, and that it never runs on a row that matched fuzzy
- 10The five formulas, and that each one carries the settings chosen on the page formula
- 10Every number printed on this page, checked against the code behind it figures
- 9What would match more rows, and why a given row did not diagnose
- 10Stacking: not one row lost, not one invented, and no column shifted stack
- 8Every icon and image is the real brand asset, at the size the page claims brand
- 8The writing itself: sentence rhythm, repeated openers, and the templates that give generated prose away humanise
- 7The downloads, including the unmatched rows from both sides and the report export
What it will not do
- It will not guess which key you meantIt ranks the columns by how unique and how complete they are and suggests the best one. You pick.
- It will not merge the contents of two cellsWhere both files have a column of the same name, both are kept under different names. Deciding which value is right is not a decision a tool should make for you.
- It will not fuzzy match silentlyFuzzy matching is off until you switch it on, every match it makes is listed with its score, and where two candidates tie it refuses rather than picking one.
- It will not join three files at onceJoin two, download the result, join it to the third. Each pass keeps its own key and its own report.
- It will not clean your dataThe settings here touch the key and nothing else. What comes out in the other columns is exactly what went in.
Questions people ask first
What is the difference between merging and joining?
They are two different jobs that share one word. Stacking puts one file under the other: more rows, the same columns, which is what you want when an export was split by month or by region. Joining puts them side by side on a shared key: the same rows, more columns, which is what VLOOKUP does. Most tools that rank for "merge CSV" only stack, which is why so many people search for merge, get a stacker, and conclude the tool is broken. Both are here, with a switch at the top.
Why did my rows not match?
Almost always the key rather than the data. Trailing spaces, different capitalisation, an id that lost its leading zeros when a spreadsheet opened it, a long number turned into scientific notation, or a company written as Acme Inc on one side and Acme, Inc. on the other. Rather than listing those causes and leaving you to guess, this page switches each one on behind the scenes, runs the join again, and tells you how many more rows it would match. For the rows still stuck it names the closest key on the other side with a similarity score.
Why did the join give me more rows than I started with?
Because a key repeats on the other side. If an order id appears twice in the second file, every matching row from the first comes out twice. Every database behaves that way, and it still catches people out. Repeated keys are counted on both sides before the join runs, the worst one is named with how many times it appears, and the expected row count is worked out from the keys so the number you get is never a shock.
Can I join on more than one column?
Yes. Tick more than one column on each side and they are joined as a composite key, first name plus postcode for instance. The parts are kept apart internally, so a row with "ab" and "c" can never collide with a row with "a" and "bc", which is a real bug in tools that simply glue the values together. The number of key columns has to match on both sides.
What does fuzzy matching actually do here?
It only runs on rows that did not match exactly, and only when you turn it on. Each leftover key is compared with the keys on the other side and the closest one is taken if it is above the threshold you chose. Every match is listed with its score, so you can check the guesses instead of trusting them, and where two candidates are equally close nothing is chosen and the row is named as ambiguous. Before you turn it on, the page tells you how many rows each threshold would actually add.
Can I get the rows that did not match?
Yes, from both sides, as their own files. That is almost always the real next step: the unmatched rows are the finding, not the leftovers. They are also included as their own sheets in the Excel download, along with the join report and, if fuzzy matching was used, every match it made with its score.
Are my files uploaded?
No. Both files are read and joined by code already sitting in this browser tab, including the Excel reader, which is written out rather than pulled in from a library, so there is no third party script to audit. There is no upload endpoint and no server waiting to receive anything. Disconnect after the page has loaded and it carries on working, which is checked by a test on every run.
Can I have the formula instead?
Yes, and that is unusual, so it is worth saying what you get. The same join is written out as XLOOKUP, as VLOOKUP for older Excel and Google Sheets, as INDEX and MATCH for when the column you want sits to the left of the key, as SQL, and as pandas. Each one carries the normalisation you chose on this page, so the formula matches the same rows the preview shows, and each comes with a note about where that particular form goes wrong.
Tell us what would make this better
A pair of files that this page joins differently from the way you expected is the most useful thing anyone can send us. The shape is enough, you do not need to send the real data. toolssupport@apexautomationteam.com
- A join that came out wrong, with the key columns you used
- A way keys disagree that is not on the settings list yet
- A layout your spreadsheet or BI tool wants that is not here
- A version of this wired into your own platform or workflow
- Something else altogether, built to your spec
Email our team See what we build
A row that joined to the wrong record jumps the queue. Everything else is a feature request and waits its turn.