datarekha
SQL Medium Asked at AmazonAsked at Google

How do you join tables on multiple keys, and why is the key order in a composite index important?

The short answer

You combine conditions in the ON clause with AND to join on multiple columns, which is necessary when no single column is a unique identifier across both tables. For index performance, the most selective column — or the column used in equality predicates — should come first in a composite index.

How to think about it

Multi-key joins show up in partitioned tables, slowly changing dimensions, and any schema where no single column uniquely identifies a row. The interviewer is checking two things: whether you know when a composite key is required, and whether you understand how index column order drives performance.

The governing principle: every condition in the ON clause must match simultaneously. There’s no partial credit — one mismatched column means no join for that row pair.

A worked example — match on three columns

Joining sales to budgets on region + product + year, and computing the variance. Only rows matching all three survive:

SELECT s.region, s.product_id, s.year,
       s.revenue, b.budget,
       s.revenue - b.budget AS variance
FROM sales s
JOIN budgets b
  ON s.region     = b.region
 AND s.product_id = b.product_id
 AND s.year       = b.year
ORDER BY s.region, s.product_id;
regionproduct_idyearrevenuebudgetvariance
East1202450004500500
East2202430003500-500
West1202440003800200

The (West, 2, 2023) sale is silently dropped — there’s no budget row for that exact triple, and one mismatched column (the year) is enough to exclude it. Swap to LEFT JOIN and it reappears with a NULL budget. The AND chain is unforgiving by design.

Mixing equality and range conditions

You can combine equality and inequality predicates, but on most engines only the equality columns drive an index seek; the range condition then narrows the scan from that point:

SELECT a.session_id, b.event_id
FROM sessions a
JOIN events b
  ON a.customer_id = b.customer_id    -- equality: drives the seek
 AND a.status      = b.status
 AND b.event_ts BETWEEN a.start_ts AND a.end_ts;   -- range: narrows the scan

Composite index column order

An index on (region, product_id, year) can serve a lookup on region, on region + product_id, or on all three — but not on product_id alone. The leading column must appear in the predicate, or the index is useless:

CREATE INDEX idx_budgets_composite ON budgets (region, product_id, year);

NULL behaviour on multi-key joins

NULL never equals NULL in SQL. If both sides carry NULL in a join column, those rows won’t match — even though the NULLs “mean the same unknown.” For surrogate keys this rarely bites; for natural keys from upstream data, filter the NULLs out explicitly, or use IS NOT DISTINCT FROM (PostgreSQL/BigQuery) when you genuinely want NULL-equal semantics.

Learn it properly INNER JOIN

Keep practising

All SQL questions

Explore further

Skip to content