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.
| Operator | Returns | Duplicates |
|---|---|---|
UNION | ||
UNION ALL | ||
INTERSECT | ||
EXCEPT |
UNION and UNION ALL
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;City --------- NULL Bradford Harrogate Leeds York City --------- NULL Bradford Harrogate Leeds Leeds Leeds York York
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.
SELECT CustomerId, FirstName FROM dbo.CustomersUNIONSELECT OrderId FROM dbo.Orders;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.
SELECT FirstName FROM dbo.Customers WHERE CustomerId = 1UNION ALLSELECT OrderId FROM dbo.Orders WHERE OrderId = 1;Msg 245, Level 16, State 1, Line 1 Conversion failed when converting the nvarchar value 'Asha' to data type int.
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;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
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;ProductId Name
--------- --------------
2 Cordless drill
5 Sand 25 kgSELECT CustomerId FROM dbo.CustomersEXCEPTSELECT CustomerId FROM dbo.Orders;CustomerId
----------
5
7Set operators and NULL
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:
SELECT City FROM dbo.Customers WHERE CustomerId <= 5INTERSECTSELECT City FROM dbo.Customers WHERE CustomerId >= 5ORDER BY City;City ----- NULL Leeds York
Show a hint
Show the solution
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;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 setRemember
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.