SQL Sets and Set Operations
Concept. Set operators combine two query results: UNION (in either), INTERSECT (in both), EXCEPT (in the first but not the second). UNION removes duplicates; UNION ALL keeps them.
Intuition. Stack the genres Mickey played onto the genres he was recommended. UNION merges them into one deduplicated set, INTERSECT keeps the genres in both, EXCEPT keeps the ones he played but was not recommended.
Sets vs Multi-sets
Key concept: SQL tables are multi-sets (bags).
Mathematical set
- No duplicates allowed
- {1, 2, 3}: each element once
- Order doesn't matter
SQL multi-set (bag)
- Duplicates ARE allowed
- {1, 1, 2, 3, 3, 3}: elements can repeat
- Each duplicate counts separately
SQL Set Operations Overview
Figure 1. Set A and Set B are two example user sets; the panels show what each operation returns.
UNION: Combine and Remove Duplicates
Creates a true set from two queries. Duplicate rows appear only once.
-- Artists from Pop songs
SELECT DISTINCT artist -- Query A: Pop artists
FROM Songs
WHERE genre = 'Pop'
UNION -- UNION: Combines and removes duplicates
-- Artists from Rock songs
SELECT DISTINCT artist -- Query B: Rock artists
FROM Songs
WHERE genre = 'Rock' -- Result: All artists from EITHER genre
Result: Taylor Swift, Ed Sheeran (each artist appears once)
UNION ALL: Combine and Keep Duplicates
UNION ALL keeps every row, duplicates and all.
-- All Pop listens
SELECT user_id, song_id
FROM Listens l
JOIN Songs s ON l.song_id = s.song_id
WHERE genre = 'Pop' -- First result set
UNION ALL -- UNION ALL: Keeps ALL rows (duplicates too!)
-- All Classic listens
SELECT user_id, song_id
FROM Listens l
JOIN Songs s ON l.song_id = s.song_id
WHERE genre = 'Classic' -- If user listened to both, appears twice
| Operation | Duplicates | Use When |
|---|---|---|
| UNION | Removed | Want unique results |
| UNION ALL | Kept | Need all rows for counting/aggregation |
Performance: UNION ALL skips the dedup pass, so it avoids the sort UNION needs.
INTERSECT: Find Common Rows
Returns only rows that appear in BOTH queries.
-- Users who listened to Pop
SELECT DISTINCT user_id
FROM Listens l
JOIN Songs s ON l.song_id = s.song_id
WHERE genre = 'Pop' -- Query A: Pop listeners
INTERSECT -- INTERSECT: Only rows in BOTH results
-- Users who listened to Classic
SELECT DISTINCT user_id
FROM Listens l
JOIN Songs s ON l.song_id = s.song_id
WHERE genre = 'Classic' -- Result: Users who like BOTH genres
Result: Users 1, 2, and 3 (Mickey, Minnie, and Daffy all listened to both Pop and Classic).
EXCEPT: Find Difference
Returns rows from first query that are NOT in second query.
-- All songs
SELECT song_id, title FROM Songs -- Query A: All songs in database
EXCEPT -- EXCEPT: A minus B (set difference)
-- Songs that have been listened to
SELECT DISTINCT s.song_id, s.title
FROM Songs s
JOIN Listens l ON s.song_id = l.song_id -- Query B: Songs with listens
-- Result: Songs NEVER listened to
Result: Songs 3, 4, 5, 9, 10 (Shape of You, Photograph, Shivers, Bad Blood, DJ Mix). Never played.
Performance Tips
-
Index columns used in set operations for better performance
-
Rewrite as a JOIN where it fits: an INTERSECT can be rewritten as a JOIN, and EXCEPT as a LEFT JOIN ... WHERE IS NULL; on indexed keys the join is usually faster.
Practice: Multi-set vs Set Behavior
-- This table has duplicates (multi-set)
SELECT genre FROM Songs; -- Raw data: Duplicates preserved
-- Result: Pop, Pop, Rock, Rock, Rock, Classic, Classic, Classic, Rock, NULL
-- Remove duplicates to get a set
SELECT DISTINCT genre FROM Songs; -- DISTINCT: Each value once only
-- Result: Pop, Rock, Classic, NULL -- NULL counts as a distinct value!
-- UNION removes duplicates (set operation)
SELECT genre FROM Songs WHERE artist = 'Taylor Swift'
UNION -- Set operation: Removes duplicates
SELECT genre FROM Songs WHERE artist = 'Ed Sheeran';
-- Result: Pop, Rock (no duplicates)
-- UNION ALL keeps duplicates (multi-set operation)
SELECT genre FROM Songs WHERE artist = 'Taylor Swift'
UNION ALL -- Multi-set: Preserves ALL duplicates
SELECT genre FROM Songs WHERE artist = 'Ed Sheeran';
-- Result: Pop, Pop, Rock, Rock, Rock, Rock (all occurrences)