How to join two CSV files by a common column
By the Analistable team · Updated · 5 min read
To join two CSVs side by side on a shared column such as customer_id, load both into Excel with Data → From Text/CSV, then use Merge Queries. On the command line, csvjoin -c customer_id --left orders.csv customers.csv (csvkit) does it in one line, as does DuckDB: SELECT * FROM 'orders.csv' LEFT JOIN 'customers.csv' USING (customer_id).
Part of our guide: How to merge CSV files into one
The files
| orders.csv | customers.csv | |||
|---|---|---|---|---|
| order_id | customer_id | amount | customer_id | name |
| O-9001 | C-101 | 120 | C-101 | Bakery Lune |
| O-9002 | C-103 | 75 | C-102 | Hart & Co |
| O-9004 | C-104 | 60 | C-103 | Nordic Supply |
In Excel
- Data → Get Data → From File → From Text/CSV, choose orders.csv, then Load To → Only Create Connection. Repeat for customers.csv.
- Data → Get Data → Combine Queries → Merge. Select orders and customers, click customer_id in each.
- Choose Left Outer to keep every order, then expand the name column and Close & Load.
Set customer_id to Text in both queries before merging, so IDs with leading zeros match. More on join kinds: joining two tables in Excel.
On the command line
# csvkit (pip install csvkit)
csvjoin -c customer_id --left orders.csv customers.csv > joined.csv
# DuckDB
duckdb -c "COPY (SELECT * FROM 'orders.csv' LEFT JOIN 'customers.csv' USING (customer_id)) TO 'joined.csv' (HEADER)"csvjoin does an inner join by default; --left, --right and --outer change it. DuckDB uses normal SQL join types and is much faster on large files.
In Python
import pandas as pd
orders = pd.read_csv("orders.csv", dtype={"customer_id": str})
customers = pd.read_csv("customers.csv", dtype={"customer_id": str})
orders.merge(customers, on="customer_id", how="left").to_csv("joined.csv", index=False)| order_id | customer_id | amount | name |
|---|---|---|---|
| O-9001 | C-101 | 120 | Bakery Lune |
| O-9002 | C-103 | 75 | Nordic Supply |
| O-9004 | C-104 | 60 |
Joining is different from stacking. To put rows from several CSVs with the same columns under each other, see merging CSV files.
Common problems
- Leading zeros: an ID like 00123 becomes 123 when read as a number. Read key columns as text in every tool.
- Different column names for the same key (
customer_idvsCustomerID): rename one, or useleft_on/right_onin pandas andON a.customer_id = b.CustomerIDin SQL. - Duplicate keys in the lookup file multiply rows in the result. Check with
csvstator a COUNTIF before joining. - Encodings and delimiters: semicolon-separated or non-UTF-8 files need the delimiter and encoding set explicitly.
Inner, left and full joins on the same files
Using the three orders and three customers above:
| Join | Rows | What you get |
|---|---|---|
| Inner | 2 | O-9001 and O-9002, the orders with a known customer |
| Left (orders first) | 3 | All orders; O-9004 has an empty name |
| Full outer | 4 | All orders plus Hart & Co (C-102), who has no order in this file |
| Anti (orders with no customer) | 1 | O-9004 |
-- DuckDB: orders whose customer is missing from customers.csv
SELECT o.*
FROM 'orders.csv' o
ANTI JOIN 'customers.csv' c USING (customer_id);
-- Portable SQL version
SELECT o.*
FROM 'orders.csv' o
LEFT JOIN 'customers.csv' c ON c.customer_id = o.customer_id
WHERE c.customer_id IS NULL;Joining on two columns
If neither file has a unique single key (for example a price list per region and SKU), join on both columns: csvjoin -c "region,sku" with csvkit, on=["region", "sku"] in pandas, USING (region, sku) in DuckDB, and Ctrl+click both columns in the Power Query Merge dialog. Joining on SKU alone would attach every region's price to each row and multiply them.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| No rows match | One file's key has a byte-order mark or quotes in the header | Open in a text editor; rename the header, or set encoding to UTF-8 with BOM handling |
| Some IDs match, others don't | Trailing spaces or mixed case | Trim and upper-case both keys before joining |
| Result has more rows than orders.csv | customers.csv repeats a customer_id | De-duplicate customers first |
| Numbers like 1.2E+10 in the output | Long IDs read as numbers by Excel | Import as text with Data → From Text/CSV and set the type |
| Dates shift or swap day and month | Locale differences between files | Read dates as text, then parse with the right format |
Which tool when
- Excel Power Query: when the result feeds an Excel report and you want to refresh it next month.
- csvkit: quick one-off joins of small files on the command line.
- DuckDB: large files, several conditions, or SQL you already know.
- pandas: when the join is one step in a longer Python cleaning script.
Check every join the same way: count rows before and after, and count the rows with an empty value in a column from the second file. With the sample files, a left join must return 3 rows with 1 empty name; anything else means duplicates or a key mismatch.
In Google Sheets
Import both files into one spreadsheet with File → Import → Upload, choosing Insert new sheet(s) so each CSV gets its own tab. Then, in a new column on the orders tab:
=ARRAYFORMULA(IF(B2:B = "", , XLOOKUP(B2:B, customers!A2:A, customers!B2:B, "Not found")))Column B of orders holds customer_id; the formula fills a name for every order and "Not found" for C-104. Google Sheets handles files up to its cell limit per spreadsheet, so very large CSVs are better joined with DuckDB or pandas.
Frequently asked questions
- How do I merge two CSV files by a column?
- Use Excel Power Query Merge Queries, csvjoin from csvkit, pandas merge or a DuckDB SQL join on the shared column.
- Why don't my CSV rows match?
- The key is probably read as a number in one file and as text in the other, or contains spaces. Read it as text and trim it.
- Can I join CSV files without Excel?
- Yes, with csvkit or DuckDB on the command line, or with pandas in Python.
- Can I join two CSV files in Google Sheets?
- Yes. Import both into one spreadsheet (File → Import), then use VLOOKUP or XLOOKUP inside ARRAYFORMULA to bring columns from one tab into the other.
- How do I join CSV files on two columns?
- Pass both columns as the key: csvjoin -c "region,sku", on=["region", "sku"] in pandas, USING (region, sku) in DuckDB, or Ctrl+click both columns in Power Query's Merge dialog.
- How do I find rows in one CSV that aren't in the other?
- Do a left join and keep rows where a column from the second file is empty, or use ANTI JOIN in DuckDB.