Your college has two lists: students who registered on the website, and students who registered at the physical counter. The placement cell wants one combined list. Notice this is not a join — you're not matching rows side by side, you're stacking two lists on top of each other. That vertical stacking is what set operations do: UNION combines, INTERSECT keeps the common part, EXCEPT subtracts. Venn-diagram thinking, in SQL.
UNION vs UNION ALL — the duplicate question
SELECT email FROM customers
UNION
SELECT email FROM newsletter_subscribers;Result
a@x.com b@x.com c@x.com d@x.com
b@x.com existed in BOTH tables, but appears once — UNION de-duplicates the combined result.
Swap in UNION ALL and you get every row from both sides, duplicates included. Two consequences worth internalising: UNION ALL is faster (no sort/hash needed to hunt duplicates), and sometimes duplicates are correct data — two genuine logins by the same user on the same day are two events, and UNION would silently merge them. Default to UNION ALL unless you specifically need de-duplication; that one sentence wins the interview follow-up about performance.
The column rules — same shape on both sides
Set operations stack rows, so both SELECTs must produce the same shape:
- Same number of columns, in the same left-to-right order — a 3-column SELECT cannot UNION a 4-column one.
- Compatible types position by position — INT with DECIMAL is fine; INT with a random VARCHAR is an error (or a nasty implicit conversion).
- Output column names come from the first SELECT — aliases in the second are politely ignored.
INTERSECT — only what's in both
SELECT product_id FROM online_orders
INTERSECT
SELECT product_id FROM store_orders;Result
2 3
Products ordered through BOTH channels. Duplicates within a side don't produce duplicates in the output — INTERSECT returns distinct rows.
One MySQL history note interviewers love: native INTERSECT arrived only in MySQL 8.0.31. On older versions you emulate it with IN or EXISTS plus an explicit DISTINCT:
SELECT DISTINCT o.product_id
FROM online_orders o
WHERE EXISTS (
SELECT 1 FROM store_orders s WHERE s.product_id = o.product_id
);EXCEPT — the first list minus the second
"Customers who ordered in 2025 but not in 2026" — churn analysis in one operator:
SELECT customer_id FROM orders_2025
EXCEPT
SELECT customer_id FROM orders_2026;Order matters — A EXCEPT B and B EXCEPT A are different questions (lost customers vs new customers). Oracle spells this operator MINUS; same behaviour, different keyword — a near-guaranteed one-mark question. And notice EXCEPT is the third face of a pattern you already own: the anti-join (LEFT JOIN … IS NULL) from lesson 2 and NOT EXISTS from lesson 4 answer the same question with different tools.
Wait — NULLs behave differently here
You know from lesson 1 that NULL = NULL is UNKNOWN, never TRUE. But watch this: UNION two copies of the row (1, NULL) — you get one row back. For the specific purpose of duplicate elimination, set operators treat two NULLs as matching — the same special rule GROUP BY and DISTINCT use. Ordinary comparisons say "unknown"; duplicate-checks say "same". Knowing that inconsistency exists — and where — is a hard-tier interview answer delivered in one sentence.
Mixing operators — who runs first?
-- Without parentheses this means A UNION (B INTERSECT C),
-- because INTERSECT binds tighter than UNION/EXCEPT:
SELECT id FROM a
UNION
SELECT id FROM b
INTERSECT
SELECT id FROM c;INTERSECT has higher precedence (like AND over OR); UNION and EXCEPT among themselves evaluate left to right. The professional habit: always write the parentheses — your intent should never depend on the reader remembering precedence tables.
Common mistakes
- Using UNION out of habit when UNION ALL is both correct and faster — or worse, letting UNION silently merge genuinely duplicate events.
- Expecting column matching by name — set operators match by position only.
- Putting
ORDER BYon each SELECT — only one ORDER BY is allowed, at the very end, sorting the combined result. - Writing A EXCEPT B when the question asked for B EXCEPT A — read twice, subtract once.
- Mixing UNION and INTERSECT without parentheses and getting a different query than you meant.
Quick recap
| Operator | Returns | Duplicates? |
|---|---|---|
UNION | rows from either side | removed |
UNION ALL | rows from either side | kept — and it's faster |
INTERSECT | rows present in both | removed (MySQL 8.0.31+) |
EXCEPT / MINUS | first side minus second | removed (order matters!) |
Practice Zone — PYQs from real selection rounds
Query A returns 3 rows, query B returns 2 rows, and exactly one row is identical in both. How many rows do UNION and UNION ALL return, respectively?
Asked in


What happens when you UNION a SELECT returning 3 columns with a SELECT returning 4 columns?
Asked in


Your MySQL version is older than 8.0.31, so INTERSECT isn't available. What's the standard emulation?
Asked in


Two rows (1, NULL) come from the two sides of a UNION. Does the result contain one row or two?
Asked in


Without parentheses, how does standard SQL evaluate A UNION B INTERSECT C?
Asked in


Given web_logins and mobile_logins, write a query using UNION ALL to produce a combined login log that preserves every individual login event, including exact duplicates across the two tables.
Asked in


web_logins2 rows
| user_id | login_date |
|---|---|
| 1 | 2026-01-01 |
| 2 | 2026-01-02 |
mobile_logins2 rows
| user_id | login_date |
|---|---|
| 1 | 2026-01-01 |
| 3 | 2026-01-03 |
Given orders_2025(customer_id) and orders_2026(customer_id), write a query using EXCEPT to find customers who placed an order in 2025 but have not placed any order in 2026.
Asked in


orders_20253 rows
| customer_id |
|---|
| 1 |
| 2 |
| 3 |
orders_20262 rows
| customer_id |
|---|
| 2 |
| 4 |
Given team_a_members(emp_id) and team_b_members(emp_id), write a query that returns the symmetric difference — employee IDs that belong to exactly one of the two teams, not both — using EXCEPT and UNION.
Asked in


team_a_members3 rows
| emp_id |
|---|
| 1 |
| 2 |
| 3 |
team_b_members3 rows
| emp_id |
|---|
| 2 |
| 3 |
| 4 |
FAQ
UNION vs JOIN — what's the one-line difference?
JOIN combines tables horizontally (matching rows side by side into wider rows); UNION combines results vertically (stacking rows into a longer list). If your two sources have the same columns, you probably want a set operation; if they have different columns linked by a key, you want a join.
Can I ORDER BY the combined result?
Yes — one ORDER BY at the very end applies to the whole stacked result. Use the column names from the first SELECT (that's where output names come from).
Does MySQL support EXCEPT?
Since 8.0.31, yes (along with INTERSECT). On anything older, emulate with the LEFT JOIN … IS NULL anti-join or NOT EXISTS — both from earlier lessons, and both accepted answers in interviews.
Next lesson: how a table protects itself from bad data — Lesson 7: Constraints & Keys →


