Join CSV Files with a Free CSV Join in Google Sheets

By Andrew Apell, creator of Flookup Data Wrangler. Updated

About the author

Andrew Apell is the creator of Flookup Data Wrangler and has built data cleaning tools for Google Sheets since 2018. The method in this post reflects work on large reconciliation tasks with a government team and continued testing with Sheets users.
Every Flookup result described here can be checked in the demo spreadsheet before purchase.
Read Our Story for the background, or contact the team with any questions about this post, including corrections, unclear steps or use cases we have not covered yet.

Key Takeaways

  • A join needs one exact key column that identifies a row in each file.
  • XLOOKUP performs a left join, COUNTIF filters for an inner join and VSTACK with UNIQUE lists every key for a full outer join.
  • Google Sheets cannot join inside QUERY, so the matching uses lookup and filter functions on separate tabs.
  • Duplicate or fuzzy keys break an exact join, which is where Flookup adds scoring and merge workflows.

Introduction

Your orders sit in one export and the customer details sit in another. Answering a simple question about revenue by city needs the two side by side.

A join combines the files on a shared key. A spreadsheet can do the same work without extra software, as long as the key is consistent and the columns are planned.

This guide builds each join type with native Google Sheets functions, then shows where an exact key stops being enough.


How a Join Works in a Spreadsheet

Every join answers three questions:

The third question is the difference between an inner, left, right and full outer join. All four use the same lookup; only the filter around it changes.

Matching is exact. Nairobi and nairobi are different keys, and so are an address with a trailing space and the same address without one. Clean the key before joining rather than after.


Import Both Files as Tabs

Create one spreadsheet and import the first file into a tab called Orders and the second into a tab called Customers. Keep the header row at the top of each tab and the data underneath.

Give the key column the same name on both tabs. The name itself does not matter to the formulas, but a consistent name makes the result easier to read and stops a column being matched by accident.

Use File, then Import, then Upload, and choose Insert new sheet so the import does not overwrite the tab you are working on. Repeat for the second file.

Before writing a formula, trim both key columns. A stray space at the end of an email is invisible on screen and turns a match into a miss for every lookup below.


A Left Join with XLOOKUP

A left join keeps every row from the first tab and adds the columns from the second tab where the key matches. On the Orders tab, put this formula in the first empty column beside the data:

=XLOOKUP($B2, Customers!$A:$A, Customers!$B:$B, "no match")

The first argument is the order email, the second is the key column on the Customers tab and the third is the column to return. The fourth argument is the value to show when the key is absent, which is what keeps the row instead of producing an error.

Fill the formula down, then repeat it for each additional column you want, changing the third argument each time. A result of no match is a finding rather than a failure: it names the orders whose customer is missing from the second export.

XLOOKUP is the modern replacement for VLOOKUP here because it does not care which side of the lookup column the return column sits on, and the default value removes the need for IFERROR.


An Inner Join with COUNTIF

An inner join keeps only rows whose key appears on both sides. The simplest way to test that is to count the key in the other tab:

=FILTER(A2:B, COUNTIF(Customers!$A:$A, A2:A) > 0)

COUNTIF returns a count for every key in the Orders tab. FILTER keeps the rows where the count is above zero, so an order with no customer disappears. Use the same formula with the comparison reversed to list the rows that had no match:

=FILTER(A2:B, COUNTIF(Customers!$A:$A, A2:A) = 0)

That second formula is the anti-join, and it is often more useful than the join itself. It answers the question that a reconciliation report exists to answer: what is missing?

If you want the customer columns as well, add the XLOOKUP columns from the previous section first and filter on the whole block rather than on two columns.


A Full Outer Join with VSTACK and UNIQUE

A full outer join keeps every key from both tabs. Start by stacking both key columns and removing the duplicates:

=LET(keys, UNIQUE(VSTACK(Orders!B2:B, Customers!A2:A)), FILTER(keys, LEN(keys) > 0))

The FILTER removes the blank that the empty rows at the bottom of each range would otherwise contribute. The result is one list of every email that appears in either file.

Put that list in G1, then pull the order side and the customer side into the columns beside it:

=XLOOKUP(G2, Orders!$B:$B, Orders!$A:$A, "")
=XLOOKUP(G2, Customers!$A:$A, Customers!$B:$B, "")

Blank cells in the order column are customers who never ordered, and blank cells in the customer column are orders with no customer. A right join is the same construction with the tab order reversed.

This is the join that shows the whole picture. An inner join would have hidden both kinds of gap.


The Four Join Types at a Glance

Join type Rows kept Formula approach
Inner Keys present in both tabs FILTER with COUNTIF above zero
Left Every row from the first tab XLOOKUP with a default value
Right Every row from the second tab XLOOKUP with the tabs swapped
Full outer Every key from both tabs VSTACK, UNIQUE and two XLOOKUPs

The naming comes from database queries, where the same four operations are written as a single statement. The spreadsheet version spreads them across a few formulas, which makes each step visible and editable.


A Worked Example

The Orders tab holds two records:

order_id email
1001 alice@example.com
1002 bob@example.com

The Customers tab holds two records with one shared key:

email city plan
alice@example.com Nairobi Pro
carol@example.com Kampala Team

Each join type produces a different result from the same two files.

Join Rows returned What is visible
Inner Order 1001 with Nairobi and Pro Only the pair that matched
Left Orders 1001 and 1002 Bob has no city or plan
Right Alice and Carol Carol has no order
Full outer Alice, Bob and Carol Both gaps appear in one table

The inner join is the tidy answer. The full outer join is the honest one, because it shows that Bob exists without customer details and Carol exists without an order.


Duplicate and Mismatched Keys

An exact join assumes each key appears once on each side. Real exports do not always agree.

The first three are preparation problems and a few helper columns fix them. The fourth needs fuzzy matching, which compares strings by similarity rather than equality and returns a score you can review.


Beyond Two Files

The formulas in this guide join two tabs on one exact key. That covers a large share of spreadsheet work, and it keeps the logic visible to anyone who opens the file.

The limit arrives when the keys are inconsistent or the join is only the first step in a longer clean-up. Flookup's fuzzy match scores near-duplicates, merge row combines records that belong together and the workflow templates bundle the sequence for vendor, order and user-list reconciliation.

When the files are too large for a spreadsheet, split them before the upload and join the parts that matter.


Joining Files Is a Key Problem

A join is one of the most common data tasks there is, and a spreadsheet already has the functions to perform it. Import both files, choose the key, and let XLOOKUP, COUNTIF or VSTACK carry the rows into one table.

Choose the join type by asking what should happen to a row with no match. If the answer matters, a full outer join shows it; if it does not, an inner join keeps the result small.

When the key itself is unreliable, Flookup Data Wrangler adds fuzzy matching and merge workflows beside the same data.


Frequently Asked Questions

How do I join two CSV files in Google Sheets?

Import each file as its own tab and match rows on a shared key. XLOOKUP pulls columns from the second tab for a left join, COUNTIF filters to keys present on both sides for an inner join, and VSTACK with UNIQUE lists every key for a full outer join.

Does the QUERY function support joins?

No. QUERY reads one range at a time and has no join clause. Matching across two tabs uses XLOOKUP, MATCH, COUNTIF or FILTER instead.

How do I do a left join in Google Sheets?

Keep the first tab as it is and add a lookup column beside it. XLOOKUP against the second tab returns the matching value and a default such as no match when the key is absent, which keeps every row from the first file.

How do I find rows with no match?

Count the key in the other tab with COUNTIF. A count of zero marks a row with no match, so FILTER can list those rows on their own.

What is the difference between a join and VLOOKUP?

A join compares two whole tables and decides what happens to unmatched rows. VLOOKUP is the single-cell lookup that performs the matching step inside a join.


When the Key Does Not Match Exactly

An exact join is reliable only while the keys are exact. Flookup Data Wrangler scores near-duplicates, merges records that belong together and leaves the key column clean for the next join.

Install Flookup Data Wrangler from the Google Workspace Marketplace and follow the get-started guide, or see the pricing page for the one-time passes.


Run This Workflow on Your Own Data

Flookup runs inside Google Sheets: fuzzy matching, deduplication, profiling and enrichment on the rows you already have. Free tier from install with no credit card required.

Install Flookup Data Wrangler from the Google Workspace Marketplace, or open the demo spreadsheet to run the formulas on sample data.


You Might Also Like