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.
An animated explainer · runs fully offline · no tracking
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
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.
SUM(amount) get bigger than it should be?”NULL = NULL not match, but GROUP BY puts them together?”What a Venn diagram cannot draw
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
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
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
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.
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.
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.
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.
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.
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.
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