SQL Joins, Explained Simply: How Tables Combine on a Shared Column

Customers and Orders tables linked by a shared customer_id column, illustrating how a SQL join matches rows

SQL joins combine rows from two tables by matching a column that both tables share. If your customers live in one table and their orders live in another, a join is how you see who bought what, without copying anything by hand.

This guide walks through the idea one step at a time: why data is split into separate tables, how the matching column connects them, what INNER JOIN and LEFT JOIN each keep, how the database runs a join, and the three slips that cause most join bugs.

In short
  • A join combines rows from two tables wherever a shared column has the same value.
  • Databases split data so each fact is stored once; joins bring it back together.
  • The ON condition tells SQL which columns must match.
  • INNER JOIN keeps only rows with a match on both sides; LEFT JOIN keeps every left row and fills gaps with NULL.
  • Start with the question, then pick the matching column, then the join type.
  1. 0:00Intro
  2. 0:25Two lists, one question
  3. 1:22A join combines two tables
  4. 2:13Why data is split up
  5. 3:02The matching column connects them
  6. 3:51Pick tables and the ON column
  7. 4:44INNER JOIN keeps only matches
  8. 5:39What gets filtered out
  9. 6:33LEFT JOIN keeps every left row
  10. 7:29INNER vs LEFT: which one?
  11. 8:16How a join runs, step by step
  12. 9:04Joins are everywhere
  13. 9:58An online shop's order history
  14. 10:45Three mistakes to avoid
  15. 11:39Joins in five points
  16. 12:08Question first, then column, then join

What is a SQL join?

Imagine your boss asks which customers placed orders last month, and what their names are. Nothing is missing from the database. The names sit in a Customers table with emails, and the orders sit in an Orders table with dates and totals. The only link between them is customer_id, a number that appears in both.

You could copy and paste between the two lists, but that only works for a handful of rows. One slip and a name lands next to someone else's order, and nobody notices until the report is wrong. A join asks the database to do that matching for you, exactly, every time you run it.

Here is the definition to hold on to: a SQL join takes rows from two tables and lines them up wherever a shared column has the same value. Think of a coat check. Your number is on your ticket and on the hook holding your coat. Match the numbers, and you know which coat belongs to whom. A join also never edits your tables. It builds a temporary combined result, and the originals stay exactly as they were.

Why is data split into separate tables?

If joins take effort, why not keep everything in one big table? It sounds simpler, but it causes problems. If Ana places five orders, her name and email are repeated five times. When she changes her email, you have to fix it in many places, and sooner or later the copies drift out of sync.

Good databases are designed to store each fact once. With separate tables, Ana is stored a single time in Customers, an update touches just one row, and each of her orders simply points to her customer_id. That is the trade-off: split the data so it stays correct, then use a join whenever you need related facts side by side.

  • One big table: repeated names, many places to update, copies that drift apart
  • Separate tables: each customer stored once, one row to update, orders point to customer_id

How do tables connect? Keys and the ON condition

Split tables need something to reconnect them: a matching column that appears in both tables and means the same thing in each. In the Customers table, customer_id is the primary key, a value unique to each row. Because no two customers share it, it is safe to match on.

In the Orders table, the same value is a foreign key, because it points back to a row in another table. One customer can appear on many orders, but each order points to exactly one customer. That one-to-many relationship is what most everyday joins rely on.

Writing a join comes down to two decisions: which two tables, and which columns must match. In the query below, SELECT picks the name and order date, FROM customers names the first table, JOIN orders brings in the second, and ON says rows match when customer_id is the same in both.

Two details matter later. The table after FROM is the left table, and the table after JOIN is the right table. And ON is the matching rule. Without it, SQL has no idea which customer goes with which order.

# names and order dates together
SELECT customers.name, orders.order_date
FROM customers
JOIN orders
  ON customers.customer_id = orders.customer_id;

What does INNER JOIN do?

INNER JOIN is the most common join, and when you write plain JOIN, SQL treats it as an INNER JOIN. Its rule is strict but simple: a row appears in the result only if it has a partner on both sides.

Take three customers, Ana, Ben and Cara, and two orders. Order 101 points to customer 1 and order 102 to customer 2. Matching on ON, one equals one and two equals two, but customer 3, Cara, has no match. The result is Ana with order 101 and Ben with order 102. Cara is a real customer, but she has not ordered anything, so she disappears.

This works like a quiet filter, and SQL gives no warning when rows are dropped. It also works both ways: an order whose customer_id matches no customer, perhaps from a typo or a deleted record, is dropped too. If a result has fewer rows than you expected, compare row counts before and after the join. Unmatched data is a likely cause.

What does LEFT JOIN do?

Sometimes the rows that don't match are exactly the ones you need. A LEFT JOIN keeps every row from the left table, matched or not. In the query below, the only change is one word: LEFT JOIN instead of JOIN. The ON condition stays the same, and customers is still the left table because it comes after FROM.

Now Cara appears in the result, with NULL in her order ID column. NULL means there is no value here. It is not zero and not empty text; it is SQL's way of saying there was no match.

The order of tables matters. A LEFT JOIN keeps everything from the table you name first. Put orders first and you would keep every order rather than every customer. So before writing it, ask yourself which list must be complete.

# every customer, orders if any
SELECT customers.name, orders.order_id
FROM customers
LEFT JOIN orders
  ON customers.customer_id = orders.customer_id;

INNER JOIN vs LEFT JOIN: which should you use?

The two can use identical tables and an identical ON condition. The only difference is what happens to a row with no match on the other side: INNER JOIN drops it, LEFT JOIN keeps it and fills the gaps with NULL.

The easiest way to choose is to say the question out loud. "Show me orders with customer names" is an INNER JOIN, because you only care about things that exist together. "Show me every customer, even ones who never ordered" is a LEFT JOIN, and it is especially useful for finding what is missing. Let the question decide, not habit.

  • Keeps: INNER only matching rows; LEFT every left row
  • No match: INNER drops the row; LEFT fills it with NULL
  • Cara: missing from INNER; shown with a NULL order in LEFT
  • Best for: INNER for things that exist together; LEFT for finding what's missing

How does a join run inside the database?

You don't need the deep internals, but a simple mental model helps you predict a result before you run it. First, the database reads the left table row by row. Real databases use shortcuts such as indexes, but the result matches this simple picture.

Next, for each left row, it tests the ON condition and finds every right-table row where it is true. If Ana has two orders, that is two matches, so Ana appears twice. Finally, each match is glued into one wider row containing columns from both tables. Rows with no match are dropped by an INNER JOIN or kept with NULLs by a LEFT JOIN.

  • Read the rows of the left table
  • Test the ON condition against the right table
  • Combine matches into wider rows and return them

Where are SQL joins used?

Once you know the pattern, you'll notice it everywhere. Whenever two kinds of information appear together on a screen, a join probably built it: sales reports by customer, a profile shown alongside activity in an app, or dashboards with combined numbers. Underneath, it is the same idea: two tables, one matching column, and a join type.

Consider tapping My Orders in an online shop. The app sends a request, builds a SQL query with your customer_id, and the database joins Customers for your name with Orders for your purchases. A WHERE clause on your customer_id narrows the result, so out of every order in the shop you see only yours. The join combines; the filter narrows.

Real queries often chain three or more tables, such as customers to orders, then orders to products. Each extra join is simply one more matching column, added in the same way.

Common SQL join mistakes to avoid

Most join bugs come from three simple slips. The first is forgetting ON. Without a matching rule, some databases pair every customer with every order, producing a huge, meaningless result, while others throw an error. Write the ON condition first, before anything else.

The second is matching the wrong column. Joining orders.order_id to customers.customer_id runs without complaint, but it pairs orders with the wrong customers. Check that both columns mean the same thing, not just that both are numbers. The third is ignoring NULLs. After a LEFT JOIN, treating NULL like zero or filtering it away by accident makes your unmatched customers vanish. Look at NULLs on purpose; they are telling you something.

  • Forgetting ON: every row can pair with every row
  • Wrong column: confident results that are simply wrong
  • Ignoring NULLs: missing matches quietly disappear

Key takeaways

  • A join combines rows from two tables using a column they share.
  • Tables are split so each fact lives in one place and stays correct.
  • ON states which columns must match; the table after FROM is the left table.
  • INNER JOIN keeps only matches and silently drops the rest.
  • LEFT JOIN keeps every left row, with NULL where no match exists.
  • Recipe for every join: question first, then matching column, then join type.

Frequently asked questions

Is JOIN the same as INNER JOIN in SQL?

Yes. When you write just JOIN, SQL treats it as an INNER JOIN. It returns only rows that have a match in both tables.

Why does my join return fewer rows than expected?

An INNER JOIN quietly drops rows that have no partner on the other side, such as customers without orders or orders with an unmatched customer_id. Compare row counts before and after the join, and consider a LEFT JOIN if you need to keep unmatched rows.

What does NULL mean after a LEFT JOIN?

NULL means there was no matching row, so there is no value in that column. It is not zero and not empty text, so treat it deliberately rather than as a number.

Does the order of tables matter in a LEFT JOIN?

Yes. A LEFT JOIN keeps every row from the table named first, after FROM. Swap the tables and you keep a different set of rows, so decide which list must be complete.

Does a join change the data in my tables?

No. A join builds a temporary combined result for your query. The original tables stay exactly as they were.

What happens if I forget the ON condition?

Without a matching rule, some databases pair every row of one table with every row of the other, creating a huge, meaningless result, while others return an error. Writing the ON condition first avoids this.

Watch the full video on YouTube →

Souy Soeng

Souy Soeng

Hi there 👋, I’m Soeng Souy (StarCode Kh)
-------------------------------------------
🌱 I’m currently creating a sample Laravel and React Vue Livewire
👯 I’m looking to collaborate on open-source PHP & JavaScript projects
💬 Ask me about Laravel, MySQL, or Flutter
⚡ Fun fact: I love turning ☕️ into code!

Post a Comment

CAN FEEDBACK
Ad