Joins
How rows from several tables are combined: INNER, the OUTER family, CROSS, LATERAL and self joins, ON versus USING, ON versus WHERE placement, NULLs in join keys, and semi and anti-join patterns. Interviewers use joins as the main SQL screen because a misplaced predicate or a duplicated key silently changes the whole result set.
part ofSQLoverview, primer and where to startread it →on this pageshowhide
explore
- Join Types33 questions
- INNER JOIN5 questions
- LEFT and RIGHT OUTER JOIN4 questions
- FULL OUTER JOIN4 questions
- CROSS JOIN5 questions
- Self-Joins5 questions
- USING and NATURAL Joins5 questions
- LATERAL Joins5 questions
- Join Semantics and Pitfalls14 questions
- ON vs WHERE with Outer Joins5 questions
- Semi- and Anti-Join Patterns5 questions
- NULLs in Join Keys4 questions
- AI & Data Scientistrole
- AI Engineerrole
- BI Analystrole
- Backend Developerrole
- Cyber Security Expertrole
- Data Analystrole
- Data Engineerrole
- Full Stack Developerrole
- Java Backend Developerrole
- Java SDETrole
- Kotlin Backend Developerrole
- MLOps Engineerrole
- Machine Learning Engineerrole
- PostgreSQL DBArole
- QA Engineerrole
- SQLskill
questions
page 2 of 2In JOIN ... USING (dept_id), how must you reference dept_id, and is d.dept_id portable?
basics
~20 sThe standard makes the USING column a column of the join, referenced unqualified as dept_id. Qualifying it is not portable: Oracle rejects a qualifier on a USING column, while PostgreSQL resolves it to that table's own copy.
What can go wrong when you join on COALESCE(a.code, 'N/A') = COALESCE(b.code, 'N/A')?
basics
~20 sIt makes every NULL-keyed row on one side match every NULL-keyed row on the other, multiplying rows; and if the sentinel ever appears in real data, genuine rows join to missing ones. Wrapping both columns in a function also blocks ordinary index use.
A LEFT JOIN report lost its zero-activity rows after a WHERE date filter was added — how do you diagnose and fix it?
basics
~20 sLook for WHERE predicates naming the outer-joined alias: a date filter on the optional table discards NULL-extended rows and collapses the LEFT JOIN. Move the date range into the ON clause, then assert that every preserved-side row still appears.
How do you find staging rows absent from a target table when the match key spans two columns?
basics
~20 sUse NOT EXISTS with one correlated equality per key column, ANDed together. It is the portable multi-column anti-join and extends to any number of columns. A LEFT JOIN with both equalities in ON plus an IS NULL test on a target key column works too.
When does adding DISTINCT to a JOIN fail to reproduce the EXISTS semi-join's result?
basics
~20 sDISTINCT de-duplicates the whole projected row, so it also collapses rows the left table genuinely holds more than once, while EXISTS preserves left-side multiplicity exactly. It also stops helping as soon as a right-side column is projected, since those values differ per match.
How do you use CROSS JOIN to report zero-sale days for every product in a date range?
basics
~20 sCROSS JOIN a date list to the product list to manufacture every (day, product) pair, LEFT JOIN the sales table on both keys, then COALESCE the aggregate to 0 so pairs with no sale still report a row.
How do you reconcile two tables with a FULL OUTER JOIN and label rows missing on either side or mismatched?
basics
~20 sFull outer join the two sets on the business key, COALESCE the keys into one column, then use CASE: a NULL key on one side means the row is missing there, and otherwise compare the value columns NULL-safely to flag mismatches.
Why does adding an INNER JOIN to a report sometimes make rows disappear?
basics
~20 sBecause an inner join is also a filter. Attaching a lookup table asserts that a matching row exists, so any fact row whose key is missing, orphaned or unmatched is removed from the result — silently, with no error and no NULL placeholder.
How do you use LATERAL to return each customer's three most recent orders?
basics
~20 sDrive the query from one row per group and join a LATERAL subquery that selects from the detail table, correlates on the group key, and carries its own ORDER BY with FETCH FIRST 3 ROWS ONLY. The limit then applies per left row, not to the whole result.
Why does one LATERAL join beat three scalar correlated subqueries in a SELECT list?
basics
~20 sThree scalar subqueries are three independent per-row lookups, so on ties they can return columns from different rows and each must return at most one row. One LATERAL join picks a row once and exposes all of its columns together, consistently.
Why does a join chain that starts with LEFT JOIN lose the preserved rows once an INNER JOIN follows?
basics
~20 sA join chain is evaluated left to right, so the inner join runs against the already NULL-extended intermediate result. Its predicate compares a NULL column, evaluates to unknown, and the preserved rows are discarded — turning the whole chain inner.
Using a self-join, how do you pair each sensor reading with the previous reading for that device?
basics
~20 sA join has no notion of "the row before", so the ON clause must define it: join the table to itself on the same device with an earlier timestamp, then keep only the nearest earlier row — via a MAX subquery or a NOT EXISTS check that no reading falls in between.
A NATURAL JOIN report returned rows until a migration added created_at to both tables — why, and how do you fix it?
basics
~20 sNATURAL JOIN re-derives its condition from whatever column names the two tables currently share, so created_at silently joined the key list and rows now match only when both timestamps are equal — almost never. Replace it with an explicit ON or USING.
Why would you CROSS JOIN a table to a one-row subquery such as (SELECT SUM(amount) AS total FROM sales)?
basics
~20 sPairing N rows with a one-row subquery yields N rows, attaching that subquery's value — a grand total, say — to every row, so each row can be compared against it without repeating the aggregate query in the select list.
With FULL OUTER JOIN ... USING (product_id), what does the single product_id column hold for unmatched rows?
basics
~20 sThe merged column is the coalesce of both sides, so a row present on only one side shows that side's product_id rather than NULL. With an ON join you would have to write COALESCE(a.product_id, b.product_id) yourself.
In a LEFT JOIN, does WHERE (o.status = 'PAID' OR o.id IS NULL) equal putting the status test in ON?
basics
~20 sNo. The two agree only for left rows with no match at all. A customer whose orders are all unpaid survives the ON version as a NULL-extended row, but the OR guard drops it, because its rows carry a non-NULL o.id.
When would you standardise on LATERAL for per-row lookups across a multi-engine codebase?
basics
~20 sStandardise on it when every target engine supports it or its APPLY spelling and the queries genuinely need per-row subqueries returning several rows or columns. Otherwise prefer formulations that are portable everywhere and keep the LATERAL variants isolated behind engine-specific code.
showing 31–47 of 47