SQL Server : Transpose rows to columns
Transposing Rows to Columns in SQL Server

Learn how to effectively pivot data in SQL Server, transforming rows into columns for better reporting and analysis. This guide covers various techniques, including the PIVOT operator and conditional aggregation.
Transposing rows to columns, often referred to as 'pivoting' data, is a common requirement in SQL Server for reporting and analytical purposes. Instead of having data spread across multiple rows for a single entity, pivoting consolidates this information into a single row with distinct columns for each attribute. This article will explore different methods to achieve this transformation, focusing on SQL Server 2005 and later versions.
Understanding the Need for Pivoting
Imagine you have sales data where each sale item is a row, including the product category and the sales amount. If you want to see the total sales for each product category as separate columns (e.g., 'Electronics Sales', 'Clothing Sales', 'Books Sales') for each customer, you'll need to pivot your data. This makes it easier to compare sales across categories or to integrate with other reporting tools that expect a columnar format.
flowchart LR A[Original Data] --> B{Pivot Operation} B --> C[Transformed Data] subgraph Original Data D[CustomerID, Category, SalesAmount] D --> E(1, 'Electronics', 100) D --> F(1, 'Clothing', 50) D --> G(2, 'Electronics', 120) end subgraph Transformed Data H[CustomerID, ElectronicsSales, ClothingSales] H --> I(1, 100, 50) H --> J(2, 120, 0) end
Conceptual flow of pivoting data from rows to columns
Method 1: Using the PIVOT Operator
SQL Server 2005 introduced the PIVOT
operator, which provides a concise and readable way to transpose rows to columns. This operator is particularly useful when you have a fixed number of columns you want to pivot to, or when you can determine them dynamically. The basic syntax involves specifying the aggregation function, the column to pivot on, and the values to be used as new column headers.
CREATE TABLE Sales (
CustomerID INT,
Category VARCHAR(50),
SalesAmount DECIMAL(10, 2)
);
INSERT INTO Sales (CustomerID, Category, SalesAmount) VALUES
(1, 'Electronics', 100.00),
(1, 'Clothing', 50.00),
(1, 'Books', 25.00),
(2, 'Electronics', 120.00),
(2, 'Clothing', 75.00),
(3, 'Books', 30.00);
SELECT CustomerID, Electronics, Clothing, Books
FROM (
SELECT CustomerID, Category, SalesAmount
FROM Sales
) AS SourceTable
PIVOT (
SUM(SalesAmount)
FOR Category IN ([Electronics], [Clothing], [Books])
) AS PivotTable;
Example of using the PIVOT operator to transpose sales data.
PIVOT
operator, the column names in the FOR ... IN (...)
clause must be explicitly listed. If you have a dynamic set of categories, you'll need to construct the SQL query dynamically using dynamic SQL.Method 2: Conditional Aggregation (CASE Statement)
Before the PIVOT
operator, or when dealing with dynamic column requirements, conditional aggregation using CASE
statements within an aggregate function (like SUM
or MAX
) was the primary method for pivoting. This approach offers more flexibility, especially when the number of columns to pivot is not known beforehand or when you need more complex aggregation logic.
SELECT
CustomerID,
SUM(CASE WHEN Category = 'Electronics' THEN SalesAmount ELSE 0 END) AS ElectronicsSales,
SUM(CASE WHEN Category = 'Clothing' THEN SalesAmount ELSE 0 END) AS ClothingSales,
SUM(CASE WHEN Category = 'Books' THEN SalesAmount ELSE 0 END) AS BooksSales
FROM Sales
GROUP BY CustomerID
ORDER BY CustomerID;
Pivoting data using conditional aggregation with CASE statements.
PIVOT
operator.Dynamic Pivoting
In many real-world scenarios, the values you want to pivot on (e.g., product categories) are not fixed but come from the data itself. In such cases, you cannot hardcode the column names. Dynamic SQL is required to build the PIVOT
query string at runtime. This involves constructing the list of column names from your data and then executing the generated SQL string.
DECLARE @ColumnList VARCHAR(MAX);
DECLARE @SQLQuery VARCHAR(MAX);
SELECT @ColumnList = STUFF((
SELECT DISTINCT ',' + QUOTENAME(Category)
FROM Sales
FOR XML PATH('')
), 1, 1, '');
SET @SQLQuery = 'SELECT CustomerID, ' + @ColumnList + '
FROM (
SELECT CustomerID, Category, SalesAmount
FROM Sales
) AS SourceTable
PIVOT (
SUM(SalesAmount)
FOR Category IN (' + @ColumnList + ')
) AS PivotTable;';
EXEC (@SQLQuery);
Dynamic PIVOT query using STUFF and FOR XML PATH to generate column list.