UNION, INTERSECT, EXCEPT
Combine the results of two queries vertically: all rows, common rows, or rows only in the first.
Syntax
query1 UNION [ALL] query2
query1 INTERSECT [ALL] query2
query1 EXCEPT [ALL] query2
[ORDER BY ...] [LIMIT ...] -- applies to the combined result- Both queries must return the same number of columns with compatible types. Column names come from the first query.
UNIONremoves duplicates (extra sort/hash work);UNION ALLkeeps every row and is faster — use it unless you need de-duplication.INTERSECT: rows present in both.EXCEPT: rows in the first but not the second.- Rows are compared as whole rows, and here NULLs are treated as equal.
INTERSECTbinds tighter thanUNION/EXCEPT; use parentheses.- One
ORDER BYat the very end sorts the combined result; to sort or limit one branch, wrap it in parentheses. - Add a literal column to label where each row came from:
SELECT 'refund' AS kind, ....
Examples
SELECT 'order' AS event, order_id AS id, ordered_at AS at
FROM orders WHERE customer_id = 1
UNION ALL
SELECT 'payment', order_id, paid_at
FROM payments WHERE order_id IN (SELECT order_id FROM orders WHERE customer_id = 1)
ORDER BY at, event;One timeline from two tables; the first branch names the columns.
SELECT city FROM customers
INTERSECT
SELECT delivery_city FROM orders
ORDER BY city
LIMIT 5;Distinct cities that appear as both a customer city and a delivery city.
SELECT product_id FROM products
EXCEPT
SELECT product_id FROM orders
ORDER BY product_id;Product ids never ordered (EXCEPT also removes duplicates).
Pitfalls
Different column counts (each UNION query must have the same number of columns).
Incompatible column types in the same position (UNION types text and integer cannot be matched).
UNION when UNION ALL was meant: legitimately repeated rows are silently merged.
Putting ORDER BY inside the first branch without parentheses.
Other databases
For orientation only: SQL Quest executes PostgreSQL.
- Oracle
EXCEPT is traditionally MINUS (EXCEPT is accepted since 21c).
- BigQuery
The keyword is mandatory: UNION ALL / UNION DISTINCT, INTERSECT DISTINCT, EXCEPT DISTINCT.
- MySQL
INTERSECT and EXCEPT since 8.0.31.