A join puts tables side by side, adding columns from one table to rows of another. Sometimes you need the opposite: to put the results of two queries on top of each other, as one longer list.
Perhaps the shop has a customers table and a separate newsletter subscribers table, and marketing wants one list of everyone to contact. Or you want a single "stock alerts" report that combines out-of-stock items, low-stock items and items nobody has ordered, each found by a different query. These are jobs for set operations.
In this lesson you'll combine queries with UNION and UNION ALL, label where each row came from, sort the combined result, and use INTERSECT and EXCEPT to find what two lists have in common and what's in one but not the other.
UNION and UNION ALL
The examples use a small subscribers table alongside customers:
| id | name | city |
|---|---|---|
| 1 | Ana | Lisbon |
| 2 | Farah | Leeds |
| 3 | Ema | Porto |
| 4 | Gil | Faro |
SELECT city FROM customers
UNION
SELECT city FROM subscribers;UNIONstacks the results and removes duplicate rows, so each city appears once.UNION ALLkeeps every row, including duplicates.
UNION ALL is faster, because the database doesn't have to look for duplicates. Use it whenever duplicates are impossible or you actually want them, such as when counting.
The rules for combining
Every query in a set operation must return:
- The same number of columns.
- Columns in the same order, with compatible types: text with text, numbers with numbers.
The column names come from the first query, so put your aliases there. Columns are matched by position, not by name, so SELECT name, city stacked on SELECT city, name happily mixes names and cities together without any error.
Labels and sorting
Add a fixed value as an extra column to show where each row came from:
SELECT name, 'customer' AS source FROM customers
UNION ALL
SELECT name, 'subscriber' FROM subscribers;To sort the combined result, write one ORDER BY at the very end. It applies to the whole result, and it can only use column names or aliases from the first query. A WHERE clause, on the other hand, belongs to each individual query.
INTERSECT and EXCEPT
| Operation | Returns |
|---|---|
A UNION B | Rows in A or B (or both), once each |
A INTERSECT B | Rows in both A and B |
A EXCEPT B | Rows in A that aren't in B |
Oracle calls EXCEPT MINUS. PostgreSQL, SQLite and SQL Server support both INTERSECT and EXCEPT; MySQL added them in version 8.0.31. Like UNION, both remove duplicates. The same questions can also be answered with joins or subqueries, but set operations often read more clearly.
SQL UNION Syntax
Set operations
SELECT col1, col2 FROM a
UNION -- or UNION ALL, INTERSECT, EXCEPT
SELECT col1, col2 FROM b
ORDER BY col1;SQL UNION Summary
UNIONstacks query results and removes duplicates;UNION ALLkeeps every row and is faster.- Each query must have the same number of columns, in the same order, with compatible types.
- Column names come from the first query; one ORDER BY at the end sorts everything.
INTERSECTreturns rows found in both queries;EXCEPTreturns rows in the first but not the second.- A literal column such as
'customer' AS sourcelabels where each row came from.
SQL UNION Examples
Each example shows the code, the output it produces and what each part does.
One contact list with sources
SELECT name, 'customer' AS source FROM customers
UNION ALL
SELECT name, 'subscriber' FROM subscribers
ORDER BY name, source;name | source
------+-----------
Ana | customer
Ana | subscriber
Ben | customer
Chloe | customer
Dev | customer
Ema | customer
Ema | subscriber
Farah | subscriber
Gil | subscriberLine-by-line explanation
- Line 1The alias source is set in the first query and used for the whole result.
- Line 2UNION ALL keeps both Ana rows. With UNION they'd still both appear, because the source values differ.
- Line 4A single ORDER BY sorts the combined list.
Overlaps and differences
SELECT name FROM customers
INTERSECT
SELECT name FROM subscribers;
SELECT name FROM subscribers
EXCEPT
SELECT name FROM customers;name
-----
Ana
Ema
name
------
Farah
GilLine-by-line explanation
- Lines 1 to 3Ana and Ema are both customers and subscribers.
- Lines 5 to 7Farah and Gil subscribe but have never become customers, a list marketing would love to have.
A stock alerts report
SELECT name, 'Out of stock' AS alert FROM products WHERE stock = 0
UNION ALL
SELECT name, 'Low stock' FROM products WHERE stock BETWEEN 1 AND 20
UNION ALL
SELECT name, 'Never ordered' FROM products
WHERE id NOT IN (SELECT product_id FROM orders)
ORDER BY name, alert;name | alert
----------+--------------
Backpack | Low stock
Lunch box | Never ordered
Lunch box | Out of stockLine-by-line explanation
- Line 1Each query has its own WHERE clause.
- Line 3The backpack has 15 in stock.
- Lines 5 to 6The lunch box appears twice because it matches two different alerts.
- Line 7Sorting by name, then alert, puts each product's alerts together.
Common SQL UNION Mistakes
Different numbers of columns
SELECT name, city FROM customers
UNION
SELECT name FROM subscribers; -- error: different column countsSELECT name, city FROM customers
UNION
SELECT name, city FROM subscribers;ORDER BY inside each query
SELECT name FROM customers ORDER BY name
UNION
SELECT name FROM subscribers ORDER BY name;SELECT name FROM customers
UNION
SELECT name FROM subscribers
ORDER BY name;Using UNION when you need every row
-- counting cities: duplicates removed, so Leeds counts once
SELECT city FROM customers UNION SELECT city FROM subscribers;SELECT city FROM customers UNION ALL SELECT city FROM subscribers;SQL UNION in Real Projects
Set operations at work
Analysts use UNION ALL to stack monthly tables such as sales_2023 and sales_2024 into one dataset, and to combine several checks into a single exceptions report like the stock alerts above. EXCEPT is a quick way to compare two versions of a list, for example to see which products disappeared after an import.
SQL UNION FAQ
What is the difference between UNION and JOIN?
UNION stacks the rows of two queries on top of each other, so the result has more rows and the same columns. A JOIN places matching rows side by side, so the result has more columns. Use UNION to combine similar lists, and JOIN to combine related information about the same thing.
Is UNION ALL faster than UNION?
Yes, usually. UNION has to find and remove duplicate rows, which often means sorting or hashing the whole result. UNION ALL just appends the rows. Use UNION ALL whenever duplicates are impossible or should be kept.
Which column names does a UNION result use?
The names come from the first query. Later queries only need the same number of columns in compatible types; their own column names and aliases are ignored. Put any aliases you want to show in the first SELECT.
Does MySQL support INTERSECT and EXCEPT?
MySQL added INTERSECT and EXCEPT in version 8.0.31. Older versions need workarounds: INNER JOIN or IN for an intersection, and LEFT JOIN ... IS NULL or NOT EXISTS for a difference. PostgreSQL, SQLite and SQL Server support both. Oracle traditionally uses MINUS for EXCEPT.
Can I UNION more than two queries?
Yes. Chain as many as you need, query1 UNION ALL query2 UNION ALL query3, as long as every query has the same number of compatible columns. A single ORDER BY at the very end sorts the combined result.
SQL UNION Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Complete the query so every row from both tables is kept, including duplicates.
SELECT city FROM customers
UNION
SELECT city FROM subscribers;
What does UNION do with duplicate rows?
customers has 5 rows and subscribers has 4. How many rows does SELECT name FROM customers UNION ALL SELECT name FROM subscribers return?
Which operation returns names that appear in both lists?
Where do the column names of a UNION result come from?
SQL UNION Quiz
5 questions to check what you learned.
What must every query in a UNION have in common?
- The same table
- The same WHERE clause
- The same number of columns, in compatible types and order
- The same number of rows
Why is UNION ALL usually faster than UNION?
- It uses an index
- It skips the second query
- It doesn't need to check for and remove duplicates
- It returns fewer rows
What does
A EXCEPT Breturn?- Rows in A that aren't in B
- Rows in both A and B
- Rows in B that aren't in A
- All rows except duplicates
How many ORDER BY clauses does a sorted UNION need?
- One, at the end
- One per query
- None, it sorts automatically
- Two
Will UNION treat "Leeds" and "Leeds " (with a space) as duplicates?
- Yes
- No, rows must match exactly
- Only in MySQL
- Only with UNION ALL