<style> .reveal-up, .reveal-left, .reveal-right, .reveal-stagger > * { opacity: 1 !important; transform: none !important; } </style>
  1. Learn
  2. SQL
  3. Joins and combining tables
All lessons in this course

Lesson 22 of 54 14 minIntermediate

UNION, INTERSECT and EXCEPT

Stack and compare whole query results: UNION and UNION ALL, INTERSECT, EXCEPT, the rules both queries must follow, and how set operators treat duplicates and NULL.

Joins put columns side by side. Set operators work on whole rows instead: they take two queries with the same columns and combine their results top to bottom.

Interactive

Combining two results

Query A: products ordered in January–March. Query B: products ordered in April–June.

-- A: products ordered Jan–Mar
SELECT p.ProductId, p.Name FROM …
UNION
-- B: products ordered Apr–Jun
SELECT p.ProductId, p.Name FROM …
ORDER BY ProductId;
264AB
Only in A
2 Cordless drill5 Sand 25 kg
In both
1 Claw hammer3 Tape measure 8 m4 Cement 25 kg6 Timber 2x4 2.4 m7 Plywood sheet 18 mm8 Wood screws (200)
Only in B
9 Wall plugs (100)10 Masonry paint 10 L11 Paint roller set12 Safety goggles
ProductIdName
11Claw hammer
22Cordless drill
33Tape measure 8 m
44Cement 25 kg
55Sand 25 kg
66Timber 2x4 2.4 m
77Plywood sheet 18 mm
88Wood screws (200)
99Wall plugs (100)
1010Masonry paint 10 L
1111Paint roller set
1212Safety goggles
A has 8 rows and B has 10, but UNION returns 12: it removes duplicates, so the 6 products ordered in both periods appear once. Swapping A and B changes nothing.
OperatorReturnsDuplicates
UNIONrows from either queryremoved
UNION ALLall rows from both querieskept
INTERSECTrows found in bothremoved
EXCEPTrows of the first query not in the secondremoved

UNION and UNION ALL

Query.sql
SELECT City FROM dbo.Customers WHERE CustomerId <= 4UNIONSELECT City FROM dbo.Customers WHERE CustomerId >= 5ORDER BY City; SELECT City FROM dbo.Customers WHERE CustomerId <= 4UNION ALLSELECT City FROM dbo.Customers WHERE CustomerId >= 5ORDER BY City;
Output
City
---------
NULL
Bradford
Harrogate
Leeds
York

City
---------
NULL
Bradford
Harrogate
Leeds
Leeds
Leeds
York
York

UNION removes duplicates across and within the two results: Leeds appears three times in the input but once in the output. UNION ALL just stacks them. Removing duplicates means sorting or hashing every row, so use UNION ALL when duplicates cannot happen or do not matter.

The rules

  • Both queries return the same number of columns, matched by position, not by name.
  • Matching columns need compatible types; the result uses the type with the higher precedence.
  • Column names come from the first query.
  • There is one ORDER BY, at the very end, and it sorts the combined result.
Query.sql
SELECT CustomerId, FirstName FROM dbo.CustomersUNIONSELECT OrderId FROM dbo.Orders;
Output
Msg 205, Level 16, State 1, Line 1
All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.

Matching a text column with a number makes SQL Server convert the text to a number (number types have the higher precedence), which fails at run time:

Query.sql
SELECT FirstName FROM dbo.Customers WHERE CustomerId = 1UNION ALLSELECT OrderId FROM dbo.Orders WHERE OrderId = 1;
Output
Msg 245, Level 16, State 1, Line 1
Conversion failed when converting the nvarchar value 'Asha' to data type int.

UNION ALL is often used to build one list from different sources, with a literal column saying where each row came from. The names and the ORDER BY use the first query’s column names:

Query.sql
SELECT N'Joined' AS Event, FirstName AS Who, JoinedOn AS OnDateFROM dbo.CustomersWHERE JoinedOn >= '2024-01-01'UNION ALLSELECT N'Ordered', c.FirstName, o.OrderDateFROM dbo.Orders AS oJOIN dbo.Customers AS c ON c.CustomerId = o.CustomerIdWHERE o.OrderDate < '2024-03-01'ORDER BY OnDate;
Output
Event    Who    OnDate
-------  -----  ----------
Joined   Grace  2024-01-08
Ordered  Asha   2024-02-03
Ordered  Ben    2024-02-10
Joined   Zoë    2024-02-15

INTERSECT and EXCEPT

Which products sold in the first quarter but not in the second? Each query can be as complex as you like, joins included:

Query.sql
SELECT p.ProductId, p.NameFROM dbo.Products AS pJOIN dbo.OrderItems AS oi ON oi.ProductId = p.ProductIdJOIN dbo.Orders     AS o  ON o.OrderId    = oi.OrderIdWHERE o.OrderDate < '2024-04-01'EXCEPTSELECT p.ProductId, p.NameFROM dbo.Products AS pJOIN dbo.OrderItems AS oi ON oi.ProductId = p.ProductIdJOIN dbo.Orders     AS o  ON o.OrderId    = oi.OrderIdWHERE o.OrderDate >= '2024-04-01'ORDER BY ProductId;
Output
ProductId  Name
---------  --------------
        2  Cordless drill
        5  Sand 25 kg

EXCEPT is also the shortest way to find keys with no partner, the same question as the LEFT JOIN … IS NULL pattern from the outer joins lesson:

Query.sql
SELECT CustomerId FROM dbo.CustomersEXCEPTSELECT CustomerId FROM dbo.Orders;
Output
CustomerId
----------
         5
         7

Set operators and NULL

In WHERE and ON, NULL = NULL is unknown, so NULLs never match. Set operators are different: they treat two NULLs as the same value. Ella (customer 5) has no city and is in both halves here, so NULL is in the intersection:

Query.sql
SELECT City FROM dbo.Customers WHERE CustomerId <= 5INTERSECTSELECT City FROM dbo.Customers WHERE CustomerId >= 5ORDER BY City;
Output
City
-----
NULL
Leeds
York
Quick check

Query A returns 5 rows and query B returns 3 rows, with 2 rows in common and no duplicates within either. How many rows does A UNION ALL B return?

Quick check

Which set operator gives a different result when you swap the two queries?

Practice

Show the ProductId and Name of every product that no customer from Leeds has ever ordered, sorted by ProductId.

Show a hint

All products EXCEPT the products on orders placed by Leeds customers. The second query joins Products, OrderItems, Orders and Customers.

Show the solution
Solution.sql
SELECT ProductId, Name FROM dbo.ProductsEXCEPTSELECT p.ProductId, p.NameFROM dbo.Products AS pJOIN dbo.OrderItems AS oi ON oi.ProductId = p.ProductIdJOIN dbo.Orders     AS o  ON o.OrderId    = oi.OrderIdJOIN dbo.Customers  AS c  ON c.CustomerId = o.CustomerIdWHERE c.City = N'Leeds'ORDER BY ProductId;
Output
ProductId  Name
---------  -------------------
        4  Cement 25 kg
        5  Sand 25 kg
        6  Timber 2x4 2.4 m
        7  Plywood sheet 18 mm
        9  Wall plugs (100)
       10  Masonry paint 10 L
       11  Paint roller set

Remember

  • UNION, INTERSECT and EXCEPT remove duplicates; UNION ALL keeps every row and is faster.
  • Both queries need the same number of columns with compatible types; names come from the first query.
  • One ORDER BY at the very end sorts the combined result.
  • Only EXCEPT depends on the order of the two queries.
  • Set operators treat NULLs as equal to each other, unlike = in WHERE and ON.
0 of 54 lessons completed