Interview questions

Inner Join vs Outer Join in SQL Interviews

August 6, 2025Updated May 20, 202619 min read
What No One Tells You About Inner Join Outer Join Sql And Interview Performance

Learn the whiteboard method for inner join and outer join SQL interview questions — predict surviving rows, spot NULLs, avoid WHERE traps, and explain join.

Most candidates who struggle with inner join outer join SQL interview questions aren't struggling with the concepts. They're struggling with the gap between knowing what a join does and being able to predict, row by row, exactly which records survive. An interviewer who asks you to trace a join on a whiteboard isn't testing your vocabulary. They're testing whether you can reason through data live, without a query runner to bail you out.

That gap is what this guide closes. You'll leave with a repeatable method — one tiny schema, four join types, and a handful of traps — that lets you walk into any SQL join question and predict the output before you write a single line of code.

Give the 30-Second Answer Before You Draw Anything

State the Difference Like You Mean It

The inner join vs outer join distinction comes down to one decision: what do you do with the rows that don't find a match? An inner join throws them away. An outer join keeps them, filling missing values with NULLs. That's the whole thing. Every other detail is an elaboration on that rule.

Where candidates go wrong is reaching for a longer definition when a shorter one would be more convincing. "An inner join returns only the rows where the join condition is satisfied in both tables" is technically correct, but it sounds like a textbook. "An inner join keeps the matches; everything else gets dropped" sounds like someone who uses SQL.

How I Would Explain This in a Live SQL Interview

Here's the framework that works on a whiteboard. Start with one sentence: "An inner join keeps only the rows that match on both sides, while outer joins also keep the unmatched rows and fill the gaps with NULLs." Then add the row-survival rule: "So the question I ask before I write any join is — what do I want to happen to the rows that don't find a partner?"

In a real screening round, the moment you say "what do I want to happen to the unmatched rows" out loud, you've already separated yourself from most candidates. It signals that you think about joins as a data preservation decision, not just a syntax choice. From there, you can say: "Let me draw two small tables and trace what each join type does to the same data." That transition into an example is calm, structured, and gives you something to point at.

According to PostgreSQL's official documentation, a joined table is formed by combining two other tables according to the rules of the join type — and the rules diverge entirely on how they handle non-matching rows. That's the hinge your interview answer should turn on.

Build One Tiny Schema and Reuse It for Every Join

Two Tables Are Enough If They Are Designed Well

SQL join whiteboard questions become much easier to reason through when you stop building four separate examples and start using one schema that exposes all the interesting cases at once. The schema needs three things: a shared key, at least one row that matches across both tables, and at least one row in each table that has no match on the other side.

Here's the schema I use every time. Two tables: `customers` and `orders`.

customers

orders

Notice what's built in here. Alice (customer_id 1) has two orders — that's your one-to-many case. Bob (customer_id 2) has one order. Carol (customer_id 3) has no orders — she'll be the unmatched left row. Order 104 belongs to customer_id 4, which doesn't exist in the customers table — that's the unmatched right row. This one schema will trace every join type cleanly.

What This Looks Like in Practice

The join key is `customer_id`. Every join below runs on `customers.customer_id = orders.customer_id`. The only thing that changes is what happens to Carol and order 104.

  • Inner join: Carol disappears. Order 104 disappears. Alice appears twice (two orders).
  • Left join: Carol survives with NULLs for order columns. Order 104 still disappears.
  • Right join: Order 104 survives with NULLs for customer columns. Carol disappears.
  • Full outer join: Everyone survives. Carol shows up with NULLs on the right. Order 104 shows up with NULLs on the left.

That mental model — same tables, same key, just change the preservation rule — is what makes you sound like you actually understand joins rather than having memorized four separate definitions.

Let INNER JOIN Do the Easiest Job First

Only the Matches Survive

The inner join has the simplest row-survival rule in SQL: if a row from the left table finds at least one match in the right table on the join condition, it appears in the output. If it doesn't, it's gone. No NULLs, no placeholders, no trace. The result set can only be as large as the smaller table in a one-to-one relationship — and it's often smaller than that.

This is why inner join is the right default when you know both sides of the relationship are populated and you only care about complete records. It's also why it's the easiest join to explain in an interview: the rule has no exceptions.

What This Looks Like in Practice

Running `SELECT * FROM customers INNER JOIN orders ON customers.customer_id = orders.customer_id` against the sample schema produces:

Carol is gone — no orders, no row. Order 104 is gone — no matching customer. Alice appears twice because she has two orders. That last point matters: the output has three rows even though the customers table has three rows and the orders table has four. Neither count predicts the output. The join condition and the data distribution do.

The SQL standard defines inner join as returning the cross product of the two tables filtered to only those rows satisfying the join predicate. In plain terms: every possible pairing is considered, and only the valid ones survive.

Treat LEFT JOIN as a Promise to Keep the Left Side

The Unmatched Left Rows Do Not Vanish

A left join (also written as LEFT OUTER JOIN) makes one guarantee: every row from the left table will appear in the output, matched or not. When a left row finds a match on the right, you get a complete combined row. When it doesn't, the right-side columns are filled with NULLs. The left side is always fully preserved.

This is the join interviewers are really testing when they ask about left join right join full outer join behavior. The question isn't whether you know the name — it's whether you know that NULLs appear specifically on the right side for unmatched left rows, and that those rows are not errors. They're the point.

What This Looks Like in Practice

`SELECT * FROM customers LEFT JOIN orders ON customers.customer_id = orders.customer_id` produces:

Carol is here now. Her `order_id` and `amount` are NULL because she has no orders. Order 104 is still gone — it belongs to a customer_id that doesn't exist in the left table, and a left join makes no promises about the right side. The output has four rows: three matched (including Alice's duplicate) and one NULL-extended.

MySQL's documentation on JOIN syntax confirms this behavior: LEFT JOIN returns all records from the left table and matched records from the right table, with NULLs where there is no match.

RIGHT JOIN Is the Same Idea, Just Facing the Other Way

Right Join Is Not a Different Species

A right join keeps every row from the right table and fills in NULLs on the left side for anything that doesn't match. It's structurally identical to a left join — the only difference is which table gets the preservation guarantee. Most working analysts rewrite right joins as left joins by flipping the table order, and many interviewers will actually prefer that you say so. It shows you understand the symmetry rather than treating them as separate concepts.

What This Looks Like in Practice

`SELECT * FROM customers RIGHT JOIN orders ON customers.customer_id = orders.customer_id` produces:

Order 104 is here now, with NULLs for the customer columns because customer_id 4 doesn't exist. Carol is gone — she's a left-table-only row, and right join makes no promises about the left side. The exact same result is achievable by swapping the tables and writing a left join: `SELECT * FROM orders LEFT JOIN customers ON orders.customer_id = customers.customer_id`. In an interview, saying "I'd usually rewrite this as a left join to keep the table order consistent with the rest of the query" signals practical experience, not just textbook knowledge.

FULL OUTER JOIN Keeps Both Kinds of Missing Rows

This Is the Join That Keeps Both Gaps Visible

A full outer join combines the behavior of left and right joins: every row from both tables appears in the output, whether or not it finds a match. Matched rows are combined as usual. Unmatched left rows get NULLs on the right. Unmatched right rows get NULLs on the left. Nothing is discarded.

Full outer join is the right tool when you want the complete picture — when you need to see not just what matched, but what didn't match on either side. Data reconciliation, gap analysis, and audit queries are common use cases. It's also the join type that candidates most often describe correctly but trace incorrectly, because they forget that both kinds of NULL-extended rows appear simultaneously.

What This Looks Like in Practice

`SELECT * FROM customers FULL OUTER JOIN orders ON customers.customer_id = orders.customer_id` produces:

Five rows. Alice twice (one-to-many), Bob once (matched), Carol with right-side NULLs (unmatched left), and order 104 with left-side NULLs (unmatched right). Every row from both tables is accounted for. One practical note worth dropping in an interview: MySQL does not natively support FULL OUTER JOIN syntax. You'd emulate it with a UNION of a left join and a right join. PostgreSQL, SQL Server, and Oracle support it directly.

Count the Duplicates Before They Surprise You

One Matching Key Can Still Turn Into Many Rows

The row-explosion problem is one of the most common surprises in SQL, and it's not a bug — it's the join doing exactly what it's supposed to do. When one row in the left table matches multiple rows in the right table, the output contains one combined row for each match. A one-to-many join multiplies rows, and if you don't say that out loud in an interview, the interviewer notices.

The mistake candidates make is assuming the output will have at most as many rows as the larger input table. That's wrong. If a customer has ten orders, that customer's row appears ten times in the output. The output row count is determined by the number of matching pairs, not the size of either table.

What This Looks Like in Practice

In the sample schema, Alice has two orders. Running any join that includes both of Alice's orders produces two rows for Alice — one paired with order 101, one paired with order 102. If Alice had ten orders, she'd appear ten times. This is correct behavior. The way to say it in an interview: "I'd expect this join to produce more rows than the customers table because the relationship is one-to-many. Alice has two orders, so she appears twice in the output."

Saying that unprompted is a strong signal. It tells the interviewer you're thinking about cardinality — the number of rows a join will produce — before you run the query. According to Use The Index, Luke, join cardinality has direct implications for query performance, because larger intermediate result sets require more memory and processing time. You don't need to lecture on this in an interview, but one sentence acknowledging it shows depth.

Keep Filters in ON When You Want the Outer Rows to Live

The Filter Placement Mistake That Kills Outer Joins

Moving a filter from the ON clause to the WHERE clause seems like a cosmetic change. It isn't. In an outer join, the ON clause is evaluated during the join — it determines how rows are matched and where NULLs are placed. The WHERE clause is evaluated after the join, on the full result set. When you filter on a right-side column in the WHERE clause of a left join, you eliminate the NULL-extended rows that the outer join was supposed to preserve. You've accidentally turned your left join into an inner join.

This is the trap interviewers love. It's subtle, it looks reasonable, and it produces the wrong answer silently.

What This Looks Like in Practice

Consider filtering for orders with amount greater than 0. The ON version:

Carol still appears in the output with NULLs — the filter only affected how matches were formed, not whether unmatched left rows survive.

The WHERE version:

Carol is gone. `orders.amount` is NULL for Carol, and NULL is not greater than 0, so she fails the WHERE filter and is removed. The outer join's preservation guarantee was undone by a post-join filter.

The rule: if you want unmatched rows to survive, any condition that touches the right-side table belongs in ON, not WHERE. PostgreSQL's query documentation distinguishes explicitly between join conditions and filter conditions for exactly this reason.

Finish With the Traps Interviewers Actually Care About

Null Keys, Join Order, and Dialect Quirks

Three edge cases come up in SQL interview questions often enough to deserve a sentence each. First: NULL join keys. If `customer_id` is NULL in either table, that row will not match any row on the other side, even another NULL. In SQL, `NULL = NULL` evaluates to unknown, not true. An outer join will still preserve the NULL-keyed row from its guaranteed side, but it won't match it to anything.

Second: join order. In most query optimizers, the order you write tables in a join doesn't determine execution order — the optimizer chooses. But the logical order matters for left and right joins because it determines which side gets the preservation guarantee. Swap the tables in a left join and you've changed which unmatched rows survive.

Third: dialect support. Full outer join works in PostgreSQL, SQL Server, and Oracle. MySQL requires a UNION workaround. If you're interviewing at a company that uses MySQL heavily, knowing this and saying it calmly is a small but memorable signal.

How to Spot a Candidate Who Really Understands Joins

From a hiring-manager perspective, the signals that separate real understanding from memorized definitions are specific. A strong candidate traces the output row by row without being asked. They mention duplicates before the interviewer has to point them out. They place filters deliberately in ON or WHERE and can explain why. They say something brief but accurate about cardinality or performance without turning the answer into a 10-minute lecture.

The candidate who says "I'd expect three rows here, but actually four because Alice has two orders" — unprompted — is the one who gets a follow-up question, not a polite redirect.

What This Looks Like in Practice

Here's how a short mock exchange might go:

Interviewer: "What does this query return?" (shows a left join between customers and orders with a WHERE filter on orders.amount)

Candidate: "At first glance it looks like a left join that should preserve all customers. But the WHERE clause filters on a right-side column, which means any customer with no orders — where that column is NULL — will be dropped. So this behaves like an inner join. If the intent was to keep all customers, the filter should move into the ON clause instead."

That answer is about 60 words. It identifies the trap, explains the mechanism, and proposes the fix. It doesn't recite a definition. That's the difference between sounding prepared and sounding like you actually know SQL.

FAQ

Q: How do you explain the difference between inner join and outer join in a way that sounds confident in an interview?

Lead with the row-survival rule, not the syntax: "An inner join keeps only the rows that match on both sides. Outer joins also keep the unmatched rows and fill the gaps with NULLs." Then immediately offer to trace an example on the whiteboard. Confidence comes from having a concrete example ready, not from a longer definition.

Q: What rows appear and disappear when the same two tables are joined with inner, left, right, and full outer joins?

Using the customers/orders schema above: inner join drops Carol and order 104, keeps Alice twice. Left join adds Carol back with NULLs. Right join adds order 104 back with NULLs. Full outer join keeps everyone — Alice twice, Bob once, Carol with right-side NULLs, and order 104 with left-side NULLs. The same two tables, four different row counts.

Q: How can you predict whether an outer join will produce NULLs or duplicate rows?

NULLs appear whenever an outer join preserves an unmatched row — look for rows in the guaranteed table that have no match on the other side. Duplicates appear whenever one row on the guaranteed side matches multiple rows on the other side. Check the cardinality of the relationship before you write the join: one-to-one produces no duplicates, one-to-many does.

Q: What is the difference between filtering in ON versus filtering in WHERE after a join?

ON is evaluated during the join. WHERE is evaluated on the full result set after the join completes. For inner joins, the placement rarely changes the result. For outer joins, it matters enormously: a WHERE filter on a right-side column will eliminate the NULL-extended rows that the outer join was supposed to preserve, effectively converting it to an inner join.

Q: When should you use a left join instead of a right join in an interview solution?

Almost always. Left join and right join are logically equivalent — you can convert any right join to a left join by swapping the table order. Most teams standardize on left join for readability, so defaulting to left join in an interview solution signals practical awareness. If you encounter a right join in existing code, the right answer is usually to rewrite it as a left join rather than mix conventions.

Q: What are the most common mistakes candidates make when solving join questions on a whiteboard or live coding round?

Three mistakes dominate. First, forgetting that one-to-many relationships multiply rows — the output is larger than either input. Second, placing a filter in WHERE instead of ON and accidentally converting an outer join to an inner join. Third, assuming NULL join keys will match each other — they won't, because NULL = NULL is unknown in SQL.

Q: How do join keys, cardinality, and one-to-many relationships affect the final result set?

The join key determines which rows are considered matches. Cardinality — how many rows on each side share a key value — determines how many output rows each match produces. A one-to-one relationship produces at most one output row per input row. A one-to-many relationship produces one output row per matching pair, which can be many. Knowing this before you write the join lets you predict the output size and catch unexpected row explosions early.

Q: What performance considerations should a senior candidate mention when discussing joins?

One or two sentences is enough. Larger intermediate result sets from one-to-many joins require more memory and processing. Indexes on join keys dramatically reduce lookup time, especially in nested loop joins. Full outer joins are generally more expensive than left or inner joins because they must process both unmatched sides. Mentioning cardinality awareness — "I'd check whether this is one-to-many before running it on a large table" — signals engineering maturity without overreaching.

How Verve AI Can Help You Ace Your Data Analyst Coding Interview

The hardest part of a SQL technical round isn't knowing what a left join does. It's staying calm when the interviewer introduces a WHERE clause trap mid-explanation and you need to trace the output change in real time, out loud, without a query runner. That's a live performance skill, and it only improves through practice under realistic conditions.

Verve AI Coding Copilot is built for exactly that gap. It reads your screen as you work through a problem — whether that's a LeetCode SQL challenge, a HackerRank assessment, or a live technical round — and responds to what you're actually doing, not a generic prompt. If you're tracing a join and you miss the ON vs WHERE distinction, Verve AI Coding Copilot can surface the relevant rule in context, right when you need it. It works across CodeSignal, HackerRank, and live interview environments, and it stays invisible while it does. The Secondary Copilot feature is particularly useful for sustained SQL problems where you need to hold multiple join conditions in your head simultaneously — it keeps the relevant context visible so you can focus on reasoning rather than recall. If you want to build the row-by-row reasoning this article describes until it's automatic, Verve AI Coding Copilot gives you a practice environment that responds to your actual answers.

Conclusion

You started here because you could define a join but couldn't always predict what it would produce. That's the gap that costs candidates in technical rounds — not ignorance of the concept, but the inability to trace it live.

Now you have a method. Two tables, four join types, one schema that exposes every interesting case. You know where the NULLs land and why. You know that one-to-many relationships multiply rows and that moving a filter from ON to WHERE can silently kill an outer join. You know what a hiring manager is actually listening for when they ask you to walk through a query.

Before your next interview, take those two tables — customers and orders — and trace all four joins on paper without looking at this article. Then trace the ON vs WHERE trap. Do it out loud. The whiteboard moment you were dreading becomes straightforward the second you've already done it twice.

JM

James Miller

Career Coach

Ace your live interviews with AI support!

Get Started For Free

Available on Mac, Windows and iPhone