In this chapter
We'll connect Sales to Products for the first time — meeting all six SQL join types, and finally answering the exact dollar question this course has been building toward since Act 2.
The Problem in Real Life
"Just tell me — how much did we actually sell today?" Mike asks again. It's the exact question from three chapters ago, the one Sarah could never quite finish answering, because Sales only ever knew which product sold — never what it cost.
Sarah looks at the two tables sitting side by side: Sales, with a ProductID and nothing else useful, and Products, with the price attached to that same ID. Two tables, one shared column, one answer waiting on the other side of it.
They were never separate questions. I just never connected them.
Sarah
Two Tables, Guessing vs. Two Tables, Connected
The answer was always split across two tables
Sales knew which product sold. Products knew what it cost. Neither table alone could ever produce a dollar total.
Unconnected tables can't answer connected questions
Two related tables sitting side by side are still just two separate answers until something joins them.
An INNER JOIN can quietly erase rows
A customer with no matching order simply disappears from an INNER JOIN's result — not an error, just gone.
Matching on the wrong column silently breaks everything
ON has to compare the exact columns that actually represent the same thing across both tables.
What Does a JOIN Actually Do?
A JOIN combines rows from two tables based on a column they share, using an ON clause to say which columns must be equal — Sales' ProductID and Products' own ProductID point at the exact same thing, so a JOIN lines up each sale with the product it actually was. A table alias (like s for Sales, p for Products) keeps a query with two tables readable instead of repetitive.
SQL actually names six join types. Only a couple of them do almost all the real work in practice — the rest matter more for the specific situations they were built for, and for knowing they exist at all. Here's every one of them, one at a time — each definition sitting right above the exact query that runs it:
INNER JOIN (or just JOIN) keeps only the rows that match on both tables.
SELECT s.SaleDate, p.Name, p.PriceFROM Sales sJOIN Products p ON s.ProductID = p.ProductIDWHERE s.SaleDate = '2026-08-29';
Every sale on Aug 29th now shows the product's actual name and price — information that lived only in Products until this JOIN pulled it across.
SELECT SUM(p.Price) AS TotalRevenueFROM Sales sJOIN Products p ON s.ProductID = p.ProductIDWHERE s.SaleDate = '2026-08-29';
This is the exact number Mike asked for all the way back in the SELECT chapter. Sales alone could never produce it — the price only ever existed in Products.
LEFT JOIN keeps every row from the first (left) table, filling in NULL where nothing on the right matches.
SELECT c.Name, o.OrderIDFROM Customers cLEFT JOIN Orders o ON c.CustomerID = o.CustomerIDWHERE o.OrderID IS NULL;
An INNER JOIN here would silently drop every customer who's never placed an online order — LEFT JOIN keeps them, with NULL exactly where an order would have been, which is precisely what WHERE ... IS NULL catches.
RIGHT JOIN is the mirror image of LEFT JOIN: it keeps every row from the second (right) table instead.
SELECT c.Name, o.OrderIDFROM Orders oRIGHT JOIN Customers c ON o.CustomerID = c.CustomerIDWHERE o.OrderID IS NULL;
This returns the identical result as the LEFT JOIN above — Customers is just written on the right this time. This is exactly why RIGHT JOIN is rare in real code: whatever it can do, a LEFT JOIN with the tables swapped already does.
FULL OUTER JOIN keeps every row from both tables, matched or not, filling in NULL on whichever side has nothing.
SELECT c.Name, o.OrderIDFROM Customers cFULL OUTER JOIN Orders o ON c.CustomerID = o.CustomerID;
In this particular schema, that happens to return the same rows a LEFT JOIN would — Orders.CustomerID is a NOT NULL foreign key, so an order can never exist without pointing at a real customer. FULL OUTER JOIN's extra behavior only shows something new when the right table can genuinely have rows the left table doesn't match either.
CROSS JOIN combines every row from one table with every row from the other, with no ON clause at all.
SELECT c.Name, p.NameFROM Customers cCROSS JOIN Products p;
Eleven customers times six products, paired up — 66 rows. This is also exactly what happens by accident if an INNER JOIN's ON clause is forgotten, which is why an unexpectedly huge result set is a classic sign of a missing join condition.
SELF JOIN joins a table to itself, usually to compare rows within the same table against each other.
SELECT o1.OrderID AS FirstOrder, o2.OrderID AS SecondOrder, o1.CustomerIDFROM Orders o1JOIN Orders o2 ON o1.CustomerID = o2.CustomerID AND o1.OrderID < o2.OrderID;
Here, Orders is joined to itself to match two different order rows that share the same customer — Priya is the only one who shows up, since she's the only customer who's placed more than one order so far.
SELECT o.OrderID, o.Status, p.Name, oi.QuantityFROM Orders oJOIN OrderItems oi ON o.OrderID = oi.OrderIDJOIN Products p ON oi.ProductID = p.ProductIDWHERE o.OrderID = 301;
A JOIN isn't limited to two tables — Orders, OrderItems, and Products all connect in a single query to reconstruct exactly what Priya's order actually contains.
Notice how much of this chapter really comes down to two join types: INNER and LEFT cover almost everything GreenMart will ever actually need. RIGHT is just LEFT with the tables swapped, FULL OUTER rarely shows anything extra once a foreign key is involved, and CROSS and SELF solve narrow, specific problems rather than everyday ones.
GreenMart's tables finally act like one connected picture instead of five separate ones. The next question isn't just which rows connect — it's which products actually sell the most, which means grouping what a JOIN brings together.
Key Takeaway
A JOIN combines two tables on a shared column. INNER keeps only matches, LEFT keeps everything from one side with NULL filling the gaps, and RIGHT, FULL OUTER, CROSS, and SELF apply that same core idea to less common situations.
Why This Matters
Almost nothing interesting in a real relational database lives in a single table — a JOIN is one of the single most-used skills separating someone who can write a SELECT statement from someone who can actually answer a real business question, because real questions almost always span more than one table.
Mike finally has a real dollar figure, connected all the way from Sales to Products. The next chapter takes this one step further — not just connecting rows, but collapsing them into the best-sellers list Mike's been waiting for.
