Set Operators — UNION & UNION ALL
UNION vs UNION ALL, the column-count and type rules, output column names, ORDER BY on combined results, source columns, UNION vs JOIN, and INTERSECT / EXCEPT in MySQL.
#Introduction
Set operators combine the results of multiple queries. Though a relatively small topic, it's frequently asked in SQL interviews and widely used in enterprise reporting.
This chapter covers UNION, UNION ALL and the difference between them, the rules for using UNION, ordering combined results, combining multiple tables, real-world use cases, performance considerations and common interview questions.
#What Is a Set Operator?
A set operator combines the results of two or more SELECT queries into a single result set.
| Employee name |
|---|
| John |
| David |
| Manager name |
|---|
| Alex |
| Lisa |
Combined output: John, David, Alex, Lisa.
#UNION
UNION combines results and removes duplicate rows:
SELECT employee_name FROM employee
UNION
SELECT manager_name FROM manager;If employee contains John and David, and manager contains John and Alex:
| UNION result |
|---|
| John |
| David |
| Alex |
John appears in both tables but only once in the result. (UNION is shorthand for UNION DISTINCT.)
#UNION ALL
UNION ALL combines results without removing duplicates:
SELECT employee_name FROM employee
UNION ALL
SELECT manager_name FROM manager;| UNION ALL result |
|---|
| John |
| David |
| John |
| Alex |
Duplicates remain.
#UNION vs UNION ALL
| Feature | UNION | UNION ALL |
|---|---|---|
| Removes duplicates | Yes | No |
| Keeps duplicates | No | Yes |
| Extra duplicate-elimination work | Yes | No |
| Performance | Usually slower | Usually faster |
| Best for | Unique combined results | Combining all rows |
#Rules for UNION
Rule 1 — Same Number of Columns
-- Correct: both queries return 2 columns
SELECT employee_id, employee_name FROM employee
UNION
SELECT customer_id, customer_name FROM customer;
-- Incorrect
SELECT employee_id FROM employee
UNION
SELECT customer_id, customer_name FROM customer;ERROR 1222 (21000): The used SELECT statements have a different number of columnsRule 2 — Compatible Data Types
SELECT employee_name FROM employee
UNION
SELECT customer_name FROM customer;Both columns hold compatible character data. Corresponding columns should have compatible types — query 1 column 1 with query 2 column 1, query 1 column 2 with query 2 column 2, and so on. Columns are matched by position, not by name.
Column Names in UNION Results
The output column names are taken from the first SELECT:
SELECT employee_name AS name FROM employee
UNION
SELECT customer_name FROM customer;The output column is name — so define meaningful aliases in the first query.
#ORDER BY with UNION
ORDER BY is applied once, to the final combined result:
SELECT employee_name AS name FROM employee
UNION
SELECT customer_name FROM customer
ORDER BY name;#Combining More Than Two Queries
-- All unique cities across three tables
SELECT city FROM customer
UNION
SELECT city FROM supplier
UNION
SELECT city FROM warehouse;
-- All rows from three yearly tables; duplicates preserved
SELECT * FROM sales_2024
UNION ALL
SELECT * FROM sales_2025
UNION ALL
SELECT * FROM sales_2026;SELECT * with set operators only works safely if every table has identical columns in the same order — list the columns explicitly in production code.
#Practical Example
employee: (1, John), (2, David). customer: (101, Lisa), (102, Mary).
SELECT employee_name AS name FROM employee
UNION
SELECT customer_name FROM customer;| Name |
|---|
| John |
| David |
| Lisa |
| Mary |
Adding a Source Column
In enterprise reporting you often need to know where each row came from:
SELECT employee_id AS id,
employee_name AS name,
'EMPLOYEE' AS source
FROM employee
UNION ALL
SELECT customer_id,
customer_name,
'CUSTOMER'
FROM customer;| ID | Name | Source |
|---|---|---|
| 1 | John | EMPLOYEE |
| 2 | David | EMPLOYEE |
| 101 | Lisa | CUSTOMER |
| 102 | Mary | CUSTOMER |
Extremely useful in consolidated reports.
#Real-World Examples
-- 1. Current + archived orders
SELECT * FROM orders_current
UNION ALL
SELECT * FROM orders_archive;
-- 2. Employee directory: a unique list of people
SELECT employee_name FROM employee
UNION
SELECT contractor_name FROM contractor;
-- 3. Customer email list: unique addresses
SELECT email FROM customer
UNION
SELECT email FROM vendor;
-- 4. Regional sales report
SELECT region, revenue FROM north_sales
UNION ALL
SELECT region, revenue FROM south_sales;#Performance
UNION needs that extra duplicate-elimination step — MySQL typically builds a temporary table with a unique index to produce distinct rows.
UNION ALL skips it, so it's generally faster (and since MySQL 5.7 can often stream rows without a temporary table).
Which One Should You Use?
Enterprise systems often prefer UNION ALL when duplicates are acceptable or impossible, because unnecessary duplicate elimination adds overhead.
#UNION vs JOIN
These are often confused.
UNION combines results vertically — table A's rows (John, David) followed by table B's rows (Lisa, Mary) give one column of four rows: John, David, Lisa, Mary.
JOIN combines related columns horizontally — employee + department give rows of employee name | department name:
SELECT e.employee_name, d.department_name
FROM employee e
JOIN department d ON e.department_id = d.department_id;| UNION | JOIN |
|---|---|
| Combines rows | Combines columns |
| Vertical combination | Horizontal combination |
| Requires compatible result structures | Requires a relationship / join condition |
| Used for similar datasets | Used for related datasets |
#INTERSECT and EXCEPT in MySQL
-- MySQL 8.0.31+
SELECT employee_id FROM employee
INTERSECT
SELECT employee_id FROM manager;
SELECT employee_id FROM employee
EXCEPT
SELECT employee_id FROM manager;Emulating INTERSECT
INTERSECT = rows present in both result sets:
-- Using EXISTS
SELECT e.employee_id
FROM employee e
WHERE EXISTS (
SELECT 1
FROM manager m
WHERE m.employee_id = e.employee_id
);
-- Using INNER JOIN
SELECT DISTINCT e.employee_id
FROM employee e
INNER JOIN manager m ON e.employee_id = m.employee_id;Emulating EXCEPT
EXCEPT = rows in the first result that are not in the second:
SELECT e.employee_id
FROM employee e
WHERE NOT EXISTS (
SELECT 1
FROM manager m
WHERE m.employee_id = e.employee_id
);NOT EXISTS is safer than NOT IN when the subquery may contain NULL values.
#NULL Behavior
UNION eliminates duplicates across complete rows, and for this purpose NULLs are treated as equal:
SELECT NULL AS value
UNION
SELECT NULL; -- one row
SELECT NULL AS value
UNION ALL
SELECT NULL; -- two rows#Interview Questions
Q1Difference between UNION and UNION ALL?
| UNION | UNION ALL |
|---|---|
| Removes duplicate rows | Keeps duplicate rows |
| Extra duplicate-elimination work | No duplicate elimination |
| Usually slower | Usually faster |
Q2Why is UNION ALL faster?
Because it doesn't perform duplicate elimination — it can combine and return rows directly.
Q3What are the rules for using UNION?
All participating queries must return the same number of columns, with compatible data types in corresponding positions; use a final ORDER BY to sort the combined result.
Q4How are columns matched?
By position. In SELECT employee_id, employee_name FROM employee UNION SELECT customer_id, customer_name FROM customer, employee_id maps to customer_id and employee_name to customer_name.
Q5Which query determines the output column names?
The first SELECT. With SELECT employee_name AS person_name ... UNION SELECT customer_name ..., the output column is person_name.
Q6Can UNION combine more than two queries?
Yes:
SELECT name FROM employee
UNION
SELECT name FROM customer
UNION
SELECT name FROM vendor;Q7UNION vs JOIN?
UNION combines rows vertically; JOIN combines related columns horizontally.
Q8When should you use UNION ALL?
When duplicates are acceptable or impossible, when every row must be preserved, or when maximum performance is needed.
Q9How do you emulate INTERSECT or EXCEPT in MySQL?
On MySQL 8.0.31+ use them directly. Otherwise emulate INTERSECT with EXISTS or an inner join plus DISTINCT, and EXCEPT with NOT EXISTS (or a LEFT JOIN ... WHERE right.key IS NULL).
#Common Mistakes
- Different number of columns —
SELECT id FROM employee UNION SELECT id, name FROM customerfails because the result structures don't match. - Wrong column order — even if MySQL can convert the types, the logical mapping is wrong:
-- Wrong: names land in the id column and ids in the name column
SELECT employee_id, employee_name FROM employee
UNION
SELECT customer_name, customer_id FROM customer;
-- Correct
SELECT employee_id, employee_name FROM employee
UNION
SELECT customer_id, customer_name FROM customer;- Using
UNIONwhen duplicates don't matter —UNION ALLis usually the better choice. - Assuming
UNIONsorts the final result — never rely on implicit ordering; if order matters, useORDER BY. - Using
NOT INcarelessly forEXCEPTlogic — if the subquery returns aNULL,NOT INreturns no rows at all:
-- Risky
SELECT employee_id
FROM employee
WHERE employee_id NOT IN (SELECT employee_id FROM manager);
-- Safer
SELECT e.employee_id
FROM employee e
WHERE NOT EXISTS (
SELECT 1 FROM manager m WHERE m.employee_id = e.employee_id
);#Mini Project
Given employee (employee_id, name), contractor (contractor_id, name) and customer (customer_id, name), write queries to:
- Display all unique people using
UNION. - Display all people including duplicates using
UNION ALL. - List all unique cities from the
customerandvendortables. - Merge current and archived orders.
- Add a source column identifying where each record came from.
- Compare the execution plans of
UNIONandUNION ALL.
#Practice Problems
Easy
- Combine two employee lists.
- Use
UNION ALL. - Sort combined results.
- Remove duplicate names.
- Combine three tables.
Medium
- Merge yearly sales data.
- Merge customer and vendor email lists.
- Create a company directory.
- Replace
UNIONwithUNION ALLwhere appropriate. - Add a source column to a combined result.
Interview level
- Explain
UNIONvsUNION ALL. - Why is
UNION ALLfaster? - What are the rules for using
UNION? - Explain
UNIONvsJOIN. - How would you emulate set intersection or difference in MySQL?
#Best Practices
- Use
UNION ALLunless duplicate removal is a business requirement. - Make every
SELECTreturn the same number of columns, with matching types in the correct logical order. - Apply
ORDER BYto the final combined result. - Add a source column when consolidated reports need traceability.
- Use
NOT EXISTSfor set-difference logic, especially whenNULLs are possible. - Verify execution plans for large set-operation queries.
Set operators are common in data migration, archive reporting, multi-year reports, consolidated dashboards, customer and vendor directories, banking transaction history, enterprise reporting APIs, data reconciliation and combining active with historical datasets.
#Key Takeaways
UNIONcombines result sets and removes duplicate rows;UNION ALLkeeps them and is generally faster.- All queries must return the same number of columns with compatible types; columns are matched by position.
- Output column names come from the first
SELECT. - Use an explicit
ORDER BYwhen the final order matters. UNIONcombines rows vertically, whileJOINcombines related columns horizontally.INTERSECTandEXCEPTexist from MySQL 8.0.31; otherwise emulate them withEXISTS/NOT EXISTS.