SQL Quest

Lessons, hints, explained solutions, the in-browser PostgreSQL editor and practice answer checks in your browser are available. Graded assessments, recorded progress from answer checks and new certificates aren't available yet.

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.
  • UNION removes duplicates (extra sort/hash work); UNION ALL keeps 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.
  • INTERSECT binds tighter than UNION/EXCEPT; use parentheses.
  • One ORDER BY at 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

Stack two event types shop
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.

Cities in both / only one list shop
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.

EXCEPT starter
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.