Introduction to SQL Server PIVOT operator

PIVOT in SQL Server operator applies a functionality used to pivot a table into a table. It requires the PivotFields object to successfully transform the information in one column into multiple columns in the output. You implement this action inside a SQL SELECT query when you have a pivot table – you change the data of one column into other columns.

  • Select a base dataset for pivoting
  • You can make a table or view as a temporary result by using a CTE
  • Apply the PIVOT operator

Let’s apply these steps to the following example. The base data for pivoting is the production.products and production.categories tables.

SELECT 
    category_name, 
    product_id
FROM 
    production.products p
    INNER JOIN production.categories c 
        ON c.category_id = p.category_id

You can make a table or view as a temporary result by using a CTE

SELECT * FROM (
    SELECT 
        category_name, 
        product_id
    FROM 
        production.products p
        INNER JOIN production.categories c 
            ON c.category_id = p.category_id
) 

Apply the PIVOT operator

SELECT * FROM   
(
    SELECT 
        category_name, 
        product_id
    FROM 
        production.products p
        INNER JOIN production.categories c 
            ON c.category_id = p.category_id
) t 
PIVOT(
    COUNT(product_id) 
    FOR category_name IN (
        [Children Bicycles], 
        [Comfort Bicycles], 
        [Cruisers Bicycles], 
        [Cyclocross Bicycles], 
        [Electric Bikes], 
        [Mountain Bikes], 
        [Road Bikes])
) AS pivot_table;

Output:

You can add an additional column to the select list of the query which returns the base data. This will form a row group in the pivot table. Use a calculated column named “YEAR” to compute the current year and then append that to the end of your current date range.

SELECT * FROM   
(
    SELECT 
        category_name, 
        product_id,
        model_year
    FROM 
        production.products p
        INNER JOIN production.categories c 
            ON c.category_id = p.category_id
) t 
PIVOT(
    COUNT(product_id) 
    FOR category_name IN (
        [Children Bicycles], 
        [Comfort Bicycles], 
        [Cruisers Bicycles], 
        [Cyclocross Bicycles], 
        [Electric Bikes], 
        [Mountain Bikes], 
        [Road Bikes])
) AS pivot_table;

Output:

Generating column values

In this example, you need to type each of the category names in parentheses after the IN operator. If you use the QUOTENAME() function to generate the category name list, then you will be able to avoid this problem.

DECLARE 
    @columns NVARCHAR(MAX) = '';
SELECT 
    @columns += QUOTENAME(category_name) + ','
FROM 
    production.categories
ORDER BY 
    category_name;
SET @columns = LEFT(@columns, LEN(@columns) - 1);
PRINT @columns;

Output:

[Children Bicycles],
[Comfort Bicycles],
[Cruisers Bicycles],
[Cyclocross Bicycles],
[Electric Bikes],
[Mountain Bikes],
[Road Bikes]

In this snippet:

  • The function QUOTENAME() formats the model category name with the square brackets, for instance, [Children Bicycles]
  • The LEFT() function removes any remaining commas from the @columns string

Dynamic pivot tables

If you add a new category name to the production.categories table, you should not have to update your query. You’ll probably want to re-run this query anyway to find any other rows that now have the same category name and delete them.

I’m sure that this is a common question, so hopefully this article can answer the question for you. In order to prevent the pivot table from being built when it is not needed, you can use dynamic SQL.

In this question, instead of passing a fixed list of category names to the PIVOT operator, we construct the category name list dynamically and pass it to an SQL statement, and then execute this statement dynamically using the stored procedure sp_executesql.

DECLARE 
    @columns NVARCHAR(MAX) = '', 
    @sql     NVARCHAR(MAX) = '';

-- select the category names
SELECT 
    @columns+=QUOTENAME(category_name) + ','
FROM 
    production.categories
ORDER BY 
    category_name;

-- remove the last comma
SET @columns = LEFT(@columns, LEN(@columns) - 1);

-- construct dynamic SQL
SET @sql ='
SELECT * FROM   
(
    SELECT 
        category_name, 
        model_year,
        product_id 
    FROM 
        production.products p
        INNER JOIN production.categories c 
            ON c.category_id = p.category_id
) t 
PIVOT(
    COUNT(product_id) 
    FOR category_name IN ('+ @columns +')
) AS pivot_table;';

-- execute the dynamic SQL
EXECUTE sp_executesql @sql;