Skip to main content

SQL UNPIVOT

Introduction

When working with databases, you'll often encounter situations where data is stored in a wide format (many columns) but needs to be analyzed in a tall format (many rows). The SQL UNPIVOT operation is a powerful tool that helps you transform columns into rows, effectively reversing what the PIVOT operation does.

Think of UNPIVOT as a way to normalize denormalized data. It's particularly useful when you need to:

  • Convert reports or crosstab data back into a relational format
  • Transform spreadsheet-like data into a more database-friendly structure
  • Prepare data for certain types of analysis or visualization

Understanding UNPIVOT

The Concept

The UNPIVOT operation takes multiple columns and converts them into rows with two main components:

  1. A column that will contain the former column names
  2. A column that will contain the values from those columns

Basic Syntax

sql
SELECT column1, column2, ...
FROM table_name
UNPIVOT
(
value_column -- The column that will store the values
FOR name_column -- The column that will store the original column names
IN (column_list) -- The list of columns to be unpivoted
) AS unpivot_alias;

UNPIVOT in Action: Basic Example

Let's start with a simple example. Imagine we have a table of product sales by quarter:

Original Table: ProductSales

ProductID | Q1_2023 | Q2_2023 | Q3_2023 | Q4_2023
----------|---------|---------|---------|--------
1 | 10000 | 15000 | 12000 | 18000
2 | 8000 | 9000 | 10000 | 11000
3 | 5000 | 4000 | 4500 | 6000

To analyze this data by quarter, we can unpivot it:

sql
SELECT 
ProductID,
Quarter,
Sales
FROM ProductSales
UNPIVOT
(
Sales -- Column to store values
FOR Quarter -- Column to store original column names
IN (Q1_2023, Q2_2023, Q3_2023, Q4_2023) -- Columns to unpivot
) AS UnpivotedSales;

Result:

ProductID | Quarter | Sales
----------|---------|-------
1 | Q1_2023 | 10000
1 | Q2_2023 | 15000
1 | Q3_2023 | 12000
1 | Q4_2023 | 18000
2 | Q1_2023 | 8000
2 | Q2_2023 | 9000
2 | Q3_2023 | 10000
2 | Q4_2023 | 11000
3 | Q1_2023 | 5000
3 | Q2_2023 | 4000
3 | Q3_2023 | 4500
3 | Q4_2023 | 6000

The transformation is visualized below:

Handling NULL Values

By default, UNPIVOT excludes rows where the original column values are NULL. If you want to include NULL values, you need to use a different approach. Here's how you can handle it:

sql
SELECT 
ProductID,
Quarter,
Sales
FROM
(
SELECT
ProductID,
ISNULL(Q1_2023, 0) AS Q1_2023,
ISNULL(Q2_2023, 0) AS Q2_2023,
ISNULL(Q3_2023, 0) AS Q3_2023,
ISNULL(Q4_2023, 0) AS Q4_2023
FROM ProductSales
) p
UNPIVOT
(
Sales
FOR Quarter IN (Q1_2023, Q2_2023, Q3_2023, Q4_2023)
) AS UnpivotedSales;

UNPIVOT with Multiple Measure Columns

Sometimes you might need to unpivot multiple measure columns at once. You can do this with a combination of UNPIVOT and CROSS APPLY.

Original Table: ProductMetrics

ProductID | Q1_Sales | Q1_Profit | Q2_Sales | Q2_Profit
----------|----------|-----------|----------|----------
1 | 10000 | 3000 | 15000 | 4500
2 | 8000 | 2400 | 9000 | 2700

To unpivot this into a format with Quarter, Metric, and Value columns:

sql
SELECT 
ProductID,
SUBSTRING(Measure, 1, 2) AS Quarter,
SUBSTRING(Measure, 4, LEN(Measure)) AS Metric,
Value
FROM ProductMetrics
UNPIVOT
(
Value
FOR Measure IN (Q1_Sales, Q1_Profit, Q2_Sales, Q2_Profit)
) AS UnpivotedMetrics;

Result:

ProductID | Quarter | Metric | Value
----------|---------|--------|-------
1 | Q1 | Sales | 10000
1 | Q1 | Profit | 3000
1 | Q2 | Sales | 15000
1 | Q2 | Profit | 4500
2 | Q1 | Sales | 8000
2 | Q1 | Profit | 2400
2 | Q2 | Sales | 9000
2 | Q2 | Profit | 2700

UNPIVOT in Different Database Systems

Microsoft SQL Server

SQL Server provides native UNPIVOT support as shown in the examples above.

Oracle

Oracle's syntax is slightly different:

sql
SELECT ProductID, Quarter, Sales
FROM ProductSales
UNPIVOT (Sales FOR Quarter IN (
Q1_2023 AS 'Q1_2023',
Q2_2023 AS 'Q2_2023',
Q3_2023 AS 'Q3_2023',
Q4_2023 AS 'Q4_2023'
));

PostgreSQL and MySQL

These databases don't have a native UNPIVOT operator, but you can achieve the same result using UNION ALL:

sql
SELECT ProductID, 'Q1_2023' AS Quarter, Q1_2023 AS Sales FROM ProductSales
UNION ALL
SELECT ProductID, 'Q2_2023' AS Quarter, Q2_2023 AS Sales FROM ProductSales
UNION ALL
SELECT ProductID, 'Q3_2023' AS Quarter, Q3_2023 AS Sales FROM ProductSales
UNION ALL
SELECT ProductID, 'Q4_2023' AS Quarter, Q4_2023 AS Sales FROM ProductSales;

Real-World Application: Sales Analysis

Let's look at a practical example of how UNPIVOT can be used in a real-world scenario.

Imagine you're analyzing sales data for a retail company. You have a table with monthly sales figures for different regions:

Original Table: RegionalSales

Region    | Jan_2023 | Feb_2023 | Mar_2023 | Apr_2023 | May_2023 | Jun_2023
----------|----------|----------|----------|----------|----------|--------
North | 120000 | 115000 | 118000 | 125000 | 130000 | 140000
South | 95000 | 98000 | 102000 | 105000 | 108000 | 112000
East | 88000 | 90000 | 92000 | 94000 | 98000 | 105000
West | 110000 | 112000 | 115000 | 118000 | 122000 | 128000

To analyze sales trends over time or compare monthly performance across regions, you need to transform this wide format into a tall format:

sql
SELECT 
Region,
SUBSTRING(Month, 1, 3) AS MonthName,
SUBSTRING(Month, 5, 4) AS Year,
Sales
FROM RegionalSales
UNPIVOT
(
Sales
FOR Month IN (Jan_2023, Feb_2023, Mar_2023, Apr_2023, May_2023, Jun_2023)
) AS UnpivotedSales
ORDER BY Year, CASE
WHEN MonthName = 'Jan' THEN 1
WHEN MonthName = 'Feb' THEN 2
WHEN MonthName = 'Mar' THEN 3
WHEN MonthName = 'Apr' THEN 4
WHEN MonthName = 'May' THEN 5
WHEN MonthName = 'Jun' THEN 6
END,
Region;

This transformation allows you to:

  • Create time series visualizations for each region
  • Compare monthly performance across regions
  • Calculate month-over-month growth rates
  • Apply time-based aggregations (quarterly totals, etc.)

Using UNPIVOT for Data Cleaning

Another common use for UNPIVOT is data cleaning and preparation. Consider a scenario where you have survey data with responses in multiple columns:

Original Table: SurveyResponses

RespondentID | Question1 | Question2 | Question3 | Question4
-------------|-----------|-----------|-----------|----------
1 | 5 | 4 | 3 | 5
2 | 4 | NULL | 5 | 4
3 | 3 | 5 | 4 | 2

Unpivoting this data makes it easier to analyze:

sql
SELECT 
RespondentID,
REPLACE(Question, 'Question', '') AS QuestionNumber,
Response
FROM SurveyResponses
UNPIVOT
(
Response
FOR Question IN (Question1, Question2, Question3, Question4)
) AS UnpivotedSurvey
WHERE Response IS NOT NULL;

Result:

RespondentID | QuestionNumber | Response
-------------|----------------|--------
1 | 1 | 5
1 | 2 | 4
1 | 3 | 3
1 | 4 | 5
2 | 1 | 4
2 | 3 | 5
2 | 4 | 4
3 | 1 | 3
3 | 2 | 5
3 | 3 | 4
3 | 4 | 2

This makes it much easier to perform analyses like:

  • Average score per question
  • Distribution of responses for each question
  • Correlation between different question responses

Summary

The SQL UNPIVOT operation is a powerful tool for transforming wide format data (many columns) into tall format data (many rows). It's essential for data analysis, reporting, and data cleaning tasks.

Key takeaways:

  • UNPIVOT converts columns into rows with two main components: a column for names and a column for values
  • It's the opposite of the PIVOT operation
  • Different database systems have slightly different syntax
  • When UNPIVOT isn't available, you can use UNION ALL as an alternative
  • Real-world applications include sales analysis, survey data analysis, and data cleaning

Practice Exercises

  1. Given a table EmployeeSkills with columns EmployeeID, SQL, Python, Java, and JavaScript with values 1-5 representing skill levels, write a query to unpivot this data.

  2. You have a WeatherData table with columns CityID, Jan_Temp, Feb_Temp, Mar_Temp, etc. Write an unpivot query to analyze temperature trends over months.

  3. Challenge: For a table ProductInventory with columns ProductID, WarehouseA_Stock, WarehouseA_Capacity, WarehouseB_Stock, WarehouseB_Capacity, write a query that unpivots the data to show each warehouse's stock and capacity in separate rows.

Additional Resources



If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)