Each algorithm has a precise cost model, and the differences matter at scale.
Nested-loop join is the brute force approach: for every row in R, scan all of S looking for matches. Cost: O(∣R∣⋅∣S∣) comparisons. With two tables of 10,000 rows that is 100,000,000 probes. With indexes it degrades gracefully to O(∣R∣⋅log∣S∣), which is why small driving tables with indexed inner relations are the textbook nested-loop sweet spot.
Hash join (invented independently by DeWitt and Shapiro in the 1980s) works in two phases:
- Build: hash every row of the smaller relation R into an in-memory hash table — cost O(∣R∣).
- Probe: for each row of S, look up its key — cost O(∣S∣) expected.
Total: O(∣R∣+∣S∣) — linear. The catch is memory: if R doesn't fit in RAM, you need a Grace hash join that partitions both tables to disk first, adding I/O passes.
Sort-merge join sorts both relations on the join key (cost O(∣R∣log∣R∣+∣S∣log∣S∣)), then merges them in a single linear pass. Total: O((∣R∣+∣S∣)log(∣R∣+∣S∣)). It shines when the data arrives pre-sorted (from an index scan or a prior ORDER BY) — in that case the sort cost vanishes and you're left with the pure O(∣R∣+∣S∣) merge.
The theoretical lower bound for a general equijoin is Ω(∣R∣+∣S∣+∣output∣) — you must read every input row at least once. Hash join and sort-merge (given sorted input) both achieve this bound.
Comments
Loading comments...