A join combines rows from two tables by matching a condition (usually a key relationship). The ONLY real question every join answers is: "When a row on one side has no match on the other side, what do I do with it?"
Throw it away → INNER
Keep all left rows (pad right with NULL) → LEFT
Keep all right rows (pad left with NULL) → RIGHT
Keep everything from both → FULL OUTER
Match every row with every row, no condition → CROSS
Join a table to itself → SELF
Master that one question and you've mastered all six.
Relational databases avoid duplicating data (normalization ). Instead of storing a customer's name on every order, we store customer_id on the order and the name once in Customers. Joins reconnect that split-apart data at query time. Without joins, normalized data would be useless.
We'll use two running tables:
Employees
emp_id
name
dept_id
1
Asha
10
2
Bilal
20
3
Cara
NULL
Departments
dept_id
dept_name
10
Sales
30
Legal
Note the deliberate mismatches: emp Cara has no department; dept 30 (Legal) has no employee; dept 20 exists in Employees but not in Departments.
Returns only rows where the condition matches on BOTH sides . Unmatched rows on either side are dropped.
SELECT e . name , d . dept_name
FROM Employees e
INNER JOIN Departments d ON e . dept_id = d . dept_id ;
Result: only Asha–Sales (dept_id 10 is the sole pair present in both). Bilal (20 not in Dept), Cara (NULL), Legal (no emp) all vanish.
Definition LEFT (OUTER) JOIN
Returns all rows from the LEFT table ; matched right columns fill in, unmatched right columns become NULL .
SELECT e . name , d . dept_name
FROM Employees e
LEFT JOIN Departments d ON e . dept_id = d . dept_id ;
Result: Asha–Sales, Bilal–NULL , Cara–NULL . Every employee survives.
Definition RIGHT (OUTER) JOIN
Mirror image: all rows from the RIGHT table ; unmatched left columns become NULL.
Result: Asha–Sales, NULL–Legal . Every department survives.
Trick: A RIGHT JOIN B ≡ B LEFT JOIN A. Many style guides ban RIGHT joins for clarity.
Definition FULL OUTER JOIN
Returns all rows from BOTH tables ; NULLs fill wherever a match is missing.
Result: Asha–Sales, Bilal–NULL, Cara–NULL, NULL–Legal.
The Cartesian product : every left row paired with every right row. No ON condition. If left has m m m rows and right has n n n rows, output has m × n m \times n m × n rows.
SELECT e . name , d . dept_name FROM Employees e CROSS JOIN Departments d;
Result: 3 × 2 = 6 3 \times 2 = 6 3 × 2 = 6 rows.
A table joined to itself using two aliases. Used for hierarchies (employee → manager) or comparing rows within one table.
SELECT e . name AS emp, m . name AS manager
FROM Employees e JOIN Employees m ON e . manager_id = m . emp_id ;
For our data: INNER has 1 row. Left-unmatched = {Bilal, Cara} = 2. Right-unmatched = {Legal} = 1.
∣ LEFT ∣ = 1 + 2 = 3 , ∣ RIGHT ∣ = 1 + 1 = 2 , ∣ FULL ∣ = 1 + 2 + 1 = 4. |\text{LEFT}| = 1+2 = 3,\quad |\text{RIGHT}| = 1+1 = 2,\quad |\text{FULL}| = 1+2+1 = 4. ∣ LEFT ∣ = 1 + 2 = 3 , ∣ RIGHT ∣ = 1 + 1 = 2 , ∣ FULL ∣ = 1 + 2 + 1 = 4.
Worked example Q1: Find employees with NO department ("anti-join")
SELECT e . name
FROM Employees e
LEFT JOIN Departments d ON e . dept_id = d . dept_id
WHERE d . dept_id IS NULL ;
Why LEFT JOIN? We need to keep employees even when no department matches.
Why WHERE d.dept_id IS NULL? Padded rows have NULL in right columns — filtering on that NULL isolates the unmatched ones.
Result: Bilal, Cara .
Worked example Q2: Departments with no employees
SELECT d . dept_name
FROM Departments d
LEFT JOIN Employees e ON d . dept_id = e . dept_id
WHERE e . emp_id IS NULL ;
Why this step? Putting Departments on the LEFT keeps all departments; the NULL-test finds the orphans. Result: Legal .
Worked example Q3: Pair employees who share a department (SELF JOIN)
SELECT a . name , b . name
FROM Employees a
JOIN Employees b ON a . dept_id = b . dept_id AND a . emp_id < b . emp_id ;
Why a.emp_id < b.emp_id? Without it you'd get each pair twice (A,B and B,A) and self-pairs (A,A). The strict inequality keeps each unordered pair once.
Worked example Q4: Generate a size chart — every product × every size (CROSS)
SELECT p . name , s . size FROM Products p CROSS JOIN Sizes s;
Why CROSS? We genuinely want every combination — no matching condition exists. Useful for generating grids/calendars.
Common mistake Putting the join condition in WHERE breaks LEFT JOIN
Wrong but tempting:
SELECT e . name , d . dept_name
FROM Employees e LEFT JOIN Departments d ON e . dept_id = d . dept_id
WHERE d . dept_name = 'Sales' ;
Why it feels right: "I'm just filtering for Sales, and it's still a LEFT JOIN."
Why it's wrong: the padded NULL rows have d.dept_name = NULL, and NULL = 'Sales' is not true , so WHERE deletes them — your LEFT JOIN silently degrades into an INNER JOIN.
Fix: put right-table conditions in the ON clause: ON e.dept_id = d.dept_id AND d.dept_name = 'Sales'. ON filters before padding; WHERE filters after .
Common mistake Forgetting the ON condition = accidental CROSS JOIN
Writing FROM A, B (old comma syntax) without a WHERE A.id=B.id produces a Cartesian explosion. With 10k × 10k rows that's 100 million rows. Fix: always use explicit JOIN ... ON; the keyword forces you to state the condition.
Common mistake INNER JOIN with a nullable key hides rows
Joining on dept_id drops Cara (NULL dept) — NULL never matches anything, even NULL. Why it feels right: "She's an employee, she should appear." Fix: use LEFT JOIN if you must keep NULL-key rows.
Recall Feynman: explain to a 12-year-old
Imagine two lists. List A: kids and the lunch-table number they sit at. List B: table numbers and what food is on each table. A join matches each kid to their table's food.
INNER: only show kids who sit at a real food-table (skip the kid sitting on the floor and the empty table).
LEFT: show every kid — if a kid has no table, write "no food."
RIGHT: show every table — even empty ones say "nobody here."
FULL: show every kid AND every table, blanks where missing.
CROSS: pretend every kid could sit at every table — list all combos.
SELF: match kids to other kids at the same table, using the one list twice.
Mnemonic Remember the keepers
"INNER keeps the OVERLAP, LEFT keeps the LEFT, RIGHT keeps the RIGHT, FULL keeps it ALL, CROSS makes a MALL (everything × everything), SELF talks to itself."
For the LEFT-vs-RIGHT direction: the kept table is the one whose word you read first in A LEFT JOIN B → A is kept.
INNER JOIN returns which rows? Only rows matching the ON condition on both tables; unmatched rows on either side are dropped.
LEFT JOIN keeps all rows of which table, and fills what for misses? All rows of the LEFT table; unmatched RIGHT columns become NULL.
How do you rewrite A RIGHT JOIN B using LEFT? B LEFT JOIN A (same result, swap order).
FULL OUTER JOIN equals the union of which two joins? LEFT JOIN ∪ RIGHT JOIN.
A CROSS JOIN of m and n rows produces how many rows? m × n (Cartesian product), with no ON condition.
Every join can be derived as which sequence of operations? CROSS JOIN (Cartesian product) → FILTER by ON predicate (gives INNER) → PAD unmatched outer rows with NULL.
What is the standard pattern to find rows with NO match (anti-join)? LEFT JOIN the other table, then WHERE IS NULL.
Why does a filter on the right table in WHERE turn a LEFT JOIN into an INNER JOIN? Padded NULL rows fail the comparison (NULL = value is not true), so WHERE removes them after padding.
What clause should hold right-table conditions to preserve LEFT JOIN semantics? The ON clause (filters before padding), not WHERE (filters after).
What is a SELF JOIN used for? Joining a table to itself via two aliases — e.g. employee→manager hierarchies or comparing rows within one table.
Why does INNER JOIN ON e.dept_id = d.dept_id drop a row with NULL dept_id? NULL never equals anything (even NULL), so a NULL key matches no row.
In a self-join pairing, why add a.id < b.id? To avoid duplicate (A,B & B,A) pairs and self-pairs (A,A).
Relational Algebra — selection σ \sigma σ , projection π \pi π , Cartesian product × \times × underlie joins.
Normalization — splits data across tables; joins reassemble it.
NULL semantics in SQL — why unmatched outer rows behave the way they do.
Indexes — how the optimizer avoids real m × n m\times n m × n work (hash join, merge join, nested loop).
Foreign Keys — the relationships joins typically follow.
GROUP BY and Aggregation — often combined with joins.
Normalization splits data
Hierarchies emp to manager
Intuition Hinglish mein samjho
Dekho, JOIN ka matlab simple hai: do tables ko ek matching condition se jodna. Normalization ki wajah se hum data alag-alag tables mein todte hain (taaki repeat na ho), aur JOIN usi data ko wapas jodta hai. Sabse important sawaal sirf ek hai: jab ek side ki row ka doosri side match nahi milta, tab uska kya karein? Bas yahi pure 6 joins ko define karta hai.
INNER mein sirf wahi rows aati hain jinka dono taraf match ho — baaki gayab. LEFT mein left table ki saari rows rehti hain, right ka match na mile to NULL bhar jata hai. RIGHT ulta hai (right table puri). FULL OUTER mein dono taraf ka sab kuch, jahan match na ho wahan NULL. CROSS mein koi condition hi nahi — har left row har right row ke saath (m × n combinations). SELF mein ek hi table ko do alias se khud se jodte hain, jaise employee aur uska manager same table mein ho.
Andar se har join ek hi formula hai: pehle Cartesian product (saare pairs), phir ON condition se filter (yeh INNER deta hai), phir bachi hui unmatched rows ko NULL se pad karo. Yaad rakho — agar tumhe "no match" wali rows chahiye (anti-join), to LEFT JOIN karo aur WHERE right_key IS NULL lagao.
Ek bada mistake se bacho: LEFT JOIN mein right table ki condition kabhi WHERE mein mat daalo, warna NULL rows hat jaayengi aur tumhara LEFT JOIN chupke se INNER JOIN ban jayega. Right table ki condition hamesha ON clause mein daalo. Yeh ek trick exam aur real coding dono mein paisa banwa deti hai.