Working with SQL Joins

1. Understanding Join Types

JoinReturns
INNER JOINRows matching in both tables
LEFT [OUTER] JOINAll left rows + matched right (NULL if none)
RIGHT [OUTER] JOINAll right rows + matched left
FULL [OUTER] JOINAll rows from both, NULLs where unmatched
CROSS JOINCartesian product (m × n)
SELF JOINTable joined to itself
LATERAL (PG)Right side may reference left columns
SEMI JOINEXISTS-style, returns left rows that match
ANTI JOINNOT EXISTS-style, left rows without match

2. Using INNER JOIN

Example: INNER JOIN

SELECT o.id, c.name, o.total
FROM orders o
INNER JOIN customers c ON c.id = o.customer_id
WHERE o.placed_at >= CURRENT_DATE - INTERVAL '7 days';
PropertyBehavior
DefaultJOIN alone = INNER JOIN
USING(col)Same column name on both sides, collapses
NATURAL JOINJoins on all matching column names (fragile, avoid)

3. Using LEFT JOIN

Example: Find Customers With No Orders

SELECT c.id, c.name
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.id IS NULL;
Use CasePattern
Optional related rowsShow parent even without children
Anti-joinLEFT JOIN + WHERE right_col IS NULL
AggregatesPreserve zero-count parents

4. Using RIGHT JOIN

NoteDetail
Equivalent to LEFT JOINSwap table order → use LEFT for readability
Rarely seenMost teams standardize on LEFT JOIN

5. Using FULL OUTER JOIN

Example: Reconciliation Across Two Sources

SELECT COALESCE(a.id, b.id) AS id, a.amount AS src_a, b.amount AS src_b
FROM ledger_a a
FULL OUTER JOIN ledger_b b ON a.id = b.id
WHERE a.amount IS DISTINCT FROM b.amount;
UseDetail
Diff datasetsFind rows present in one but not the other
MySQLNo native FULL — emulate with LEFT UNION RIGHT

6. Creating CROSS JOIN

UseExample
Generate combinationsdays × products for sparse fact filling
Pair with LATERALPer-row computed subqueries
RiskAccidental cross join from missing ON → row explosion

7. Performing Self Joins

Example: Employee with Manager Name

SELECT e.name AS employee, m.name AS manager
FROM employees e
LEFT JOIN employees m ON m.id = e.manager_id;
UseExample
Hierarchyemployee → manager
ComparisonsFind pairs of rows (e.g., duplicate detection)
SequencingCompare row to previous/next via join on offset

8. Using Multiple Joins

Example: 3-Table Join

SELECT o.id, c.name, p.name AS product, oi.qty
FROM orders      o
JOIN customers   c  ON c.id  = o.customer_id
JOIN order_items oi ON oi.order_id = o.id
JOIN products    p  ON p.id  = oi.product_id
WHERE o.placed_at >= NOW() - INTERVAL '30 days';
TipDetail
Filter earlyPush selective predicates to reduce join inputs
Index FK columnsBoth sides should have indexes for hash/merge plans
Watch row multiplicationJoining two M:N can explode → use DISTINCT or aggregate

9. Optimizing Join Performance

StrategyDetail
Nested LoopBest for small inner table + index on join key
Hash JoinBuild hash on smaller side; great for large equi-joins
Merge JoinBoth inputs sorted on join key — large datasets
StatisticsRun ANALYZE; stale stats → bad plans
Join orderMost planners explore orderings; force with hints sparingly

10. Understanding Join Order and Execution Plans

ToolCommand
PostgresEXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) ...
MySQLEXPLAIN FORMAT=TREE / EXPLAIN ANALYZE
SQL ServerSET STATISTICS PROFILE ON / Actual Execution Plan
OracleEXPLAIN PLAN FOR ... ; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
Plan example:
HashJoin  cost=... rows=...
  HashCond: (oi.order_id = o.id)
  -> Seq Scan on order_items oi
  -> Hash
       -> Index Scan on orders o
            Index Cond: (placed_at >= '2026-04-20')