An animated explainer · runs fully offline · no tracking

SQL joins explained: why the Venn diagram lies — and what really happens to your rows

Every “SQL joins explained” article reaches for two overlapping circles. But a Venn diagram is a picture about sets, and a join is a pairing. It cannot draw the moment one row fans out to many, and it says nothing about the NULL that never matches. Scroll to watch what a join actually does.

The real problem

You already know what a join should do. It’s the exceptions that bite.

Nobody is surprised by a clean one-to-one join. People get burned by the cases the diagram hides: a key that repeats and quietly multiplies your row count; a row that should have appeared and vanished; a NULL that grouped with other NULLs in one breath and refused to match them in the next. These are exactly the questions asked in interviews and exactly the bugs that inflate a nightly total.

  • “Why did my report suddenly show more rows than the table has?”
  • “Why is this customer missing after the join?”
  • “Why did SUM(amount) get bigger than it should be?”
  • “Why does NULL = NULL not match, but GROUP BY puts them together?”

What a Venn diagram cannot draw

One key on the left. Three on the right. Six output rows.

A join is not a membership test — it is a pairing. When the join key is duplicated, every left row pairs with every matching right row. Two “A”s on the left and three on the right make 2 × 3 = 6 rows for that key alone. The result now has more rows than either input — the shape two circles can never represent.

! blowup Six rows out of a five-row input. Two overlapping circles have no way to show a result larger than both sets.

The other thing it hides

A NULL key never matches — not even another NULL.

Every SQL condition returns one of three answers, not two: TRUE, FALSE, or UNKNOWN. Because NULL means “unknown value,” comparing it to anything — even to another NULL — yields UNKNOWN, and an ON clause only pairs rows when the answer is TRUE. So a NULL-key row is dropped by INNER, but survives LEFT / RIGHT / FULL with the other side padded to NULL.

The twist that catches everyone

GROUP BY pulls those same NULLs together.

Here is the contradiction people trip over: an ON clause treats two NULLs as not equal, but GROUP BY treats them as the same group. The same NULLs that refused to match a moment ago now fall into one bucket. And the aggregates disagree too — COUNT(*) counts every row while COUNT(col), SUM and AVG quietly skip NULLs.

Enforced, not promised

Your tables never leave your browser — the page cannot send them.

This explainer and the visualizer it links to both ship one line that makes the privacy guarantee real rather than a policy: a Content-Security-Policy with connect-src 'none'. The browser itself refuses every outbound request — no fetch, no XHR, no WebSocket, no analytics, no fonts from a CDN. There is nothing to trust and nothing to opt out of.

Because there are no network dependencies, both pages keep working with no signal at all — load once, use offline.

What the visualizer lets you do

A real evaluator, not a picture.

Five joins, ribbon by ribbon

INNER, LEFT, RIGHT, FULL and CROSS on two editable tables. Every output row draws a ribbon back to the source rows it came from — solid for a matched pair, dashed into a NULL pad for an orphan.

The row-math strip

Live counts of matched pairs, left and right orphans, and output rows — with the “a Venn diagram cannot draw this” callout that fires the instant duplicate keys make the output larger than either input.

The NULL corner

Interactive three-valued-logic tables for =, <>, AND, OR, NOT and IS NULL, returning TRUE / FALSE / UNKNOWN — so you can see exactly why the NULL key never matched.

A GROUP BY stage

Pick a group column and an aggregate — COUNT(*), COUNT(col), SUM, AVG, MIN, MAX — and watch rows fall into bins, surfacing how NULLs group together yet skip the aggregates that ignore them.

12 gotcha scenarios

One click loads a table pair, an operation and a hand-verified result — the blowup, the orphan, the inflated SUM after a one-to-many join — with an “understood” checklist for interview-prep across multiple visits.

Export is the handoff

RFC-4180 CSV of the current result set and a printable cheat-sheet — because there is no cloud sync or sharing by design. What you make stays on your device.

Stop reading about joins. Watch them.

Open the visualizer, pick a join, edit two small tables, and draw the pairing yourself — duplicate-key blowup, NULL orphan, GROUP BY and all. It runs entirely in your browser, offline.

Open the live join visualizer →

Free, open-source, no account, no tracking · verified against SQLite