Combining Results with UNION
Progress Tracking
Log in to save this lesson and continue from where you left off.
Stacking Results, Not Joining
Before we learn how to link tables side by side, there’s another way to combine data from multiple tables: UNION.
UNION doesn’t “link” rows together. Instead, it stacks results on top of each other. Think of UNION as “add more rows to the same shape of table.”
Later, when we talk about JOINs, we’ll see the opposite pattern:
JOINs add more columns about each rowUNIONadds more rows with the same columns
For now, keep one idea in your head: UNION = same columns, more rows.
When You’ll Use UNION
UNION comes up when you have data split across tables that should really be together:
- Separate tables for different regions (
us_orders,eu_orders) - Historical data in yearly tables (
orders_2022,orders_2023) - Different data sources with the same structure (
app_signups,web_signups) - Building a lookup list from multiple places
Basic UNION Syntax
SELECT column1, column2
FROM table_a
UNION
SELECT column1, column2
FROM table_b;Simple: two SELECT statements with UNION between them. The results stack on top of each other.
A Real Example
Say you need all employees from HR and Admin in one list.
| id | first_name | last_name | department | salary | phone_number | joining_date |
|---|---|---|---|---|---|---|
| 1 | Sarah | Mitchell | HR | 95000 | 555-0101 | 2021-03-15 |
| 2 | Michael | Chen | HR | 88000 | 555-0102 | 2022-06-01 |
| 3 | Emily | Rodriguez | HR | 82500 | 2021-09-20 | |
| 4 | David | Park | HR | 80000 | 555-0104 | 2023-01-10 |
| 5 | Lisa | Thompson | HR | 65000 | 2021-04-05 |
Combine HR and Admin employees into one list using UNION. Add a type column with 'Employee' for HR and 'Admin' for Admin staff.
Notice the added type column. That’s a common pattern when you’re combining tables: add a source identifier so you know where each row came from.
Make the Friends Network Symmetric
| user_id | friend_id |
|---|---|
| 0 | 1 |
| 0 | 2 |
| 0 | 3 |
| 0 | 4 |
| 1 | 5 |
Make the friends network symmetric. For example, if 0 and 1 are friends, have the output contain both 0 and 1 under 1 and 0 respectively.
The Column Matching Rule
UNION has a strict requirement: both SELECTs must return the same number of columns, and the data types must be compatible.
-- This works: both have 2 columns (text, text)
SELECT name, email
FROM employees
UNION
SELECT name, email
FROM contractors;
-- This FAILS: different number of columns
SELECT name, email, salary
FROM employees
UNION
SELECT name, email
FROM contractors; -- Error!If one table has more columns than you need, just don’t select them. If one table is missing a column, you can add a placeholder:
SELECT name, email, salary
FROM employees
UNION
SELECT name, email, NULL AS salary
FROM contractors;Column names in the result come from the first SELECT. The second SELECT’s column names are ignored.
All Possible Wine Varieties
| id | country | description | designation | points | price | province | region_1 | region_2 | variety | winery |
|---|---|---|---|---|---|---|---|---|---|---|
| 126576 | US | Rich and round, this offers plenty of concentrated blackberry notes enveloped in warm spices and supple oak. There's a hint of green tomato leaves throughout, but the lush fruit combined with sturdy grape tannins and high acidity would pair well with fatty short ribs or braised pork. | Estate Club | 87 | 32 | Virginia | Virginia | Merlot | Veramar | |
| 127077 | Italy | This luminous sparkler offers measured aromas of yellow fruit and honey and delivers a broad, wide approach in the mouth. The wine's texture is creamy and full and there is a spicy point of effervescence on the palate. | Extra Dry | 85 | 19 | Veneto | Prosecco di Valdobbiadene | Prosecco | Varaschin | |
| 143029 | US | There are some pretty features to this wine, namely the lovely tannins and rich fruit, but it's just too soft and sweet. Tastes like blackberry jam, with extra white sugar spinkled on top. | Signature | 83 | 45 | California | Paso Robles | Central Coast | Cabernet Sauvignon | Byington |
| 98813 | US | Lovely nose, which runs from violets and cherries right into coffee and sweet chocolate. In the mouth the wine coats the palate with Napa-like intensity, only buttressed with more vivid acidity. It's both smooth and deep. The aromas are more detailed, interesting and really clean, loaded with dark flavors of soy, coffee, molasses and black cherry liqueur. | Champoux & Loess Vineyards Reserve | 93 | 100 | Washington | Washington | Washington Other | Cabernet Sauvignon | Boudreaux Cellars |
| 43172 | US | Tastes sweeter and fruitier than the 2009, which was dry, so if you like orange marmalade, pineapple tart and vanilla cookie sugariness in your Sauvignon Blancs, it's for you. | Les Pionniers | 86 | 40 | California | Sonoma County | Sonoma | Sauvignon Blanc | Capture |
| id | country | description | designation | points | price | province | region_1 | region_2 | taster_name | taster_twitter_handle | title | variety | winery |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 118040 | US | A bit too soft and thus lacks structure. Shows a good array of wild berry and briary, brambly flavors. Dry and spicy, and ready now. | The Convict Rocky Ridge Vineyard | 86 | 38 | California | Rockpile | Sonoma | Paradise Ridge 2006 The Convict Rocky Ridge Vineyard Zinfandel (Rockpile) | Zinfandel | Paradise Ridge | ||
| 59743 | Italy | Aromas of toasted oak, vanilla and a confectionary note lead the nose while the palate offers mature Golden Delicious apple, butterscotch and walnut skin alongside bracing acidity. | Mongris Riserva | 88 | 30 | Northeastern Italy | Collio | Kerin O'Keefe | @kerinokeefe | Marco Felluga 2012 Mongris Riserva Pinot Grigio (Collio) | Pinot Grigio | Marco Felluga | |
| 117951 | US | Heavy in alcohol and overripe, but entirely dry, and the black currant, chocolate and pepper flavors will play well against richly sauced barbecue. | The Caboose | 84 | 30 | California | Alexander Valley | Sonoma | Starry Night 2007 The Caboose Zinfandel (Alexander Valley) | Zinfandel | Starry Night | ||
| 10202 | France | Fragrant; dusty plum and rose aromas are followed on the palate by very fruity flavors of cherries and apple skins. The fine bead and rich mousse result in a creamy mouthfeel. It's all a bit obvious and simple, but undeniably good. | Brut Rosa | 87 | 83 | Champagne | Champagne | Joe Czerwinski | @JoeCz | Ruinart NV Brut Rosa (Champagne) | Champagne Blend | Ruinart | |
| 69268 | Germany | Layers of plush yellow peach and apricot flavors highlight this easy, crowd-pleasing kabinett. Sunny lemon acidity lends refreshment to the midpalate, cutting through all the ripeness and leading to a brisk finish. Drink now through 2019. | Kabinett | 88 | 15 | Rheinhessen | Anna Lee C. Iijima | Weinreich 2012 Kabinett Riesling (Rheinhessen) | Riesling | Weinreich |
Find all possible varieties which occur in either of the winemag datasets. Output unique variety values only. Sort records based on the variety in ascending order.
UNION vs UNION ALL
UNIONremoves duplicate rows (expensive)UNION ALLkeeps everything (fast)
| id | first_name | last_name | department | salary | phone_number | joining_date |
|---|---|---|---|---|---|---|
| 1 | Sarah | Mitchell | HR | 95000 | 555-0101 | 2021-03-15 |
| 2 | Michael | Chen | HR | 88000 | 555-0102 | 2022-06-01 |
| 3 | Emily | Rodriguez | HR | 82500 | 2021-09-20 | |
| 4 | David | Park | HR | 80000 | 555-0104 | 2023-01-10 |
| 5 | Lisa | Thompson | HR | 65000 | 2021-04-05 |
Stack the same HR department query twice using UNION ALL. How many rows appear?
In practice, UNION ALL is what you want 90% of the time. Duplicates usually matter — they represent real data. If John ordered from both the US and EU sites, you want both orders in your analysis.
Use UNION ALL unless you specifically need deduplication. It’s faster, and keeping duplicates is usually correct.
Duplicate HR Department Employees
| worker_id | first_name | last_name | salary | joining_date | department |
|---|---|---|---|---|---|
| 1 | Monika | Arora | 100000 | 2014-02-20 | HR |
| 2 | Niharika | Verma | 80000 | 2014-06-11 | Admin |
| 3 | Vishal | Singhal | 300000 | 2014-02-20 | HR |
| 4 | Amitah | Singh | 500000 | 2014-02-20 | Admin |
| 5 | Vivek | Bhati | 500000 | 2014-06-11 | Admin |
Generate a list of employees who work in the HR department, including only their first names and department in the output. Each employee should appear twice in the list, meaning their first name and department should be duplicated in the output.
Sorting UNION Results
ORDER BY goes at the very end, after all the UNIONs:
SELECT name, signup_date
FROM web_users
UNION ALL
SELECT name, signup_date
FROM app_users
ORDER BY signup_date DESC;You can’t ORDER BY in the middle. SQL processes all the UNIONs first, then sorts the combined result.
SQL Server is strict: you can only ORDER BY column names that appear in the output, or by position (ORDER BY 1, 2). PostgreSQL and MySQL are more lenient.
Pattern 1: Multi-Region Data
SELECT
order_id,
customer_id,
amount,
'US' AS region
FROM us_orders
UNION ALL
SELECT
order_id,
customer_id,
amount,
'EU' AS region
FROM eu_orders
UNION ALL
SELECT
order_id,
customer_id,
amount,
'APAC' AS region
FROM apac_orders;Pattern 2: Creating a Reference List
-- Build a list of all people we might need to contact
SELECT email
FROM customers
UNION
SELECT email
FROM newsletter_subscribers
UNION
SELECT email
FROM lead_forms;Here UNION (not ALL) makes sense because you only want each email once.
| index | number |
|---|---|
| 1 | 5 |
| 2 | 3 |
| 3 | 7 |
| 4 | 1 |
| 5 | 0 |
Stack two non-overlapping subsets of transportation_numbers. Does it matter whether you use UNION or UNION ALL here?
Key Takeaways
UNIONstacks rows vertically (vsJOIN, which combines columns horizontally)- Both
SELECTs must have the same number of columns with compatible types UNIONremoves duplicates,UNION ALLkeeps them (preferUNION ALL)ORDER BYgoes at the end, applying to the combined result- Add a source column to track where each row came from
What’s Next
Now you know how to stack data from similar tables. But most of the time, you need to combine related data from different tables — customers with their orders, products with their categories. That’s where JOINs come in, starting with INNER JOIN.