Pivot Data in T-Sql

Understanding PIVOT function in T-SQL

A PIVOT used to rotate the data from one column into multiple columns.

For your example here is a STATIC Pivot meaning you hard code the columns that you want to rotate:

create table temp
(
id int,
teamid int,
userid int,
elementid int,
phaseid int,
effort decimal(10, 5)
)

insert into temp values (1,1,1,3,5,6.74)
insert into temp values (2,1,1,3,6,8.25)
insert into temp values (3,1,1,4,1,2.23)
insert into temp values (4,1,1,4,5,6.8)
insert into temp values (5,1,1,4,6,1.5)

select elementid
, [1] as phaseid1
, [5] as phaseid5
, [6] as phaseid6
from
(
select elementid, phaseid, effort
from temp
) x
pivot
(
max(effort)
for phaseid in([1], [5], [6])
)p

Here is a SQL Demo with a working version.

This can also be done through a dynamic PIVOT where you create the list of columns dynamically and perform the PIVOT.

DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX);

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(c.phaseid)
FROM temp c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')

set @query = 'SELECT elementid, ' + @cols + ' from
(
select elementid, phaseid, effort
from temp
) x
pivot
(
max(effort)
for phaseid in (' + @cols + ')
) p '


execute(@query)

The results for both:

ELEMENTID   PHASEID1    PHASEID5    PHASEID6
3 Null 6.74 8.25
4 2.23 6.8 1.5

Convert Rows to columns using 'Pivot' in SQL Server

If you are using SQL Server 2005+, then you can use the PIVOT function to transform the data from rows into columns.

It sounds like you will need to use dynamic sql if the weeks are unknown but it is easier to see the correct code using a hard-coded version initially.

First up, here are some quick table definitions and data for use:

CREATE TABLE yt 
(
[Store] int,
[Week] int,
[xCount] int
);

INSERT INTO yt
(
[Store],
[Week], [xCount]
)
VALUES
(102, 1, 96),
(101, 1, 138),
(105, 1, 37),
(109, 1, 59),
(101, 2, 282),
(102, 2, 212),
(105, 2, 78),
(109, 2, 97),
(105, 3, 60),
(102, 3, 123),
(101, 3, 220),
(109, 3, 87);

If your values are known, then you will hard-code the query:

select *
from
(
select store, week, xCount
from yt
) src
pivot
(
sum(xcount)
for week in ([1], [2], [3])
) piv;

See SQL Demo

Then if you need to generate the week number dynamically, your code will be:

DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)

select @cols = STUFF((SELECT ',' + QUOTENAME(Week)
from yt
group by Week
order by Week
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')

set @query = 'SELECT store,' + @cols + ' from
(
select store, week, xCount
from yt
) x
pivot
(
sum(xCount)
for week in (' + @cols + ')
) p '

execute(@query);

See SQL Demo.

The dynamic version, generates the list of week numbers that should be converted to columns. Both give the same result:

| STORE |   1 |   2 |   3 |
---------------------------
| 101 | 138 | 282 | 220 |
| 102 | 96 | 212 | 123 |
| 105 | 37 | 78 | 60 |
| 109 | 59 | 97 | 87 |

How to write T-SQL query to Pivot data as below?

Using Jiacheng Gao's idea of using MAX() for pivot and some MSDN documentation, I came up with the following query :

SELECT id,empID,Eclass AS EType,[StartTime] AS StartTime,[EndTime] AS EndTime, [Type Code] AS [Type Code]
FROM
(SELECT id,empID,Eclass,Code,Etype
FROM DATA ) AS SourceTable
PIVOT
(
MAX(Etype)
FOR Code IN ([StartTime],[EndTime],[Type Code])
) AS PivotTable;

You can try it on the following fiddle.

How to pivot without knowing fixed columns in T-SQL

Demo on db<>fiddle

You can use ROW_NUMBER() to mark the number of values, e.g: 1, 2, 3.

Note that: ORDER BY [Balance] DESC to get the generated value as you wish.

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


SELECT Customer, Balance, Col = ROW_NUMBER() OVER (PARTITION BY Customer ORDER BY [Balance] DESC)
into #b
FROM #a

SELECT @columns += QUOTENAME(Col) + ','
from (SELECT DISTINCT Col FROM #b) A

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


SET @sql = 'SELECT * FROM ( SELECT Customer, Balance, Col FROM #b) src PIVOT( MAX([Balance]) FOR Col IN ('+ @columns +')) AS pivot_table;';

-- execute the dynamic SQL
EXECUTE sp_executesql @sql;

Output

Sample Image

Updated

Since concatenating strings is undocumented and unreliable. It does not always work as expected. So you should resolve with 2 solutions below

  1. Use STRING_AGG (From SQL Server 2017 and late)
SELECT STRING_AGG(QUOTENAME(Col), ', ')
from (SELECT DISTINCT Col FROM #b) A
// Output: [1], [2], [3]

  1. Use XML Extensions
DECLARE  @columns NVARCHAR(MAX) = ''
SELECT @columns = (
SELECT QUOTENAME(Col) + ', '
FROM (SELECT DISTINCT Col FROM #b) A

FOR XML PATH(''), TYPE
).value('.','varchar(max)')
SELECT @columns
// Output: [1], [2], [3],

Thanks @GarethD's comment. Check it out on db<>fiddle

SQL Server: Examples of PIVOTing String data

Remember that the MAX aggregate function will work on text as well as numbers. This query will only require the table to be scanned once.

SELECT Action,
MAX( CASE data WHEN 'View' THEN data ELSE '' END ) ViewCol,
MAX( CASE data WHEN 'Edit' THEN data ELSE '' END ) EditCol
FROM t
GROUP BY Action

How do I pivot my data using a T-SQL query

A simple PIVOT should do the trick

Select *
From YourTable
Pivot ( max( Custom_Value ) for Custom_Label in ( [Classification],[Trade] ) ) pvt

EDIT -

Just in case you have extra columns not listed above... you need to "feed" the pivot with only the required columns.

Select *
From ( Select Description
,Status
,Issue_number
,Custom_Label
,Custom_Value
From YourTable
) src
Pivot ( max( Custom_Value ) for Custom_Label in ( [Classification],[Trade] ) ) pvt

GROUP BY statement with a PIVOT command in T-SQL

Use conditional aggregation. It is much simpler:

SELECT [Load ID],
MIN(CASE WHEN [Sequence] = 1 THEN [Stop Zip] END) as [1],
MIN(CASE WHEN [Sequence] = 2 THEN [Stop Zip] END) as [2],
MIN(CASE WHEN [Sequence] = 3 THEN [Stop Zip] END) as [3],
MIN(CASE WHEN [Sequence] = 4 THEN [Stop Zip] END) as [4],
FROM TMS_Load_Stops
GROUP BY [Load ID];

The PIVOT doesn't work because you have additional columns in the table being pivoted. I just don't like the syntax or how it works, but you can also fix it by only selecting the columns you need:

SELECT [Load ID], [1], [2], [3], [4]
FROM (SELECT [Load ID], [Sequence], [Stop Zip]
FROM TMS_Load_Stops
) ls
PIVOT (
MIN([Stop Zip])
for [Sequence] IN ([1],[2],[3],[4])
) PivotTable
;

T-SQL :: List all tables, columns and pivot content

Updated to support 2016

Sample Image

DROP TABLE IF EXISTS #ColumnsToDisplay

SELECT ROW_NUMBER () OVER (ORDER BY tab.name) AS Iteration,
s.name AS SchemaName,
tab.name AS table_name,
col.column_id,
col.name AS column_name,
t.name AS data_type,
col.max_length,
col.precision AS PrecisionNumber,
CAST(NULL AS VARCHAR(MAX)) AS DataSample
INTO #ColumnsToDisplay
FROM sys.tables AS tab
JOIN sys.schemas AS s
ON s.schema_id = tab.schema_id
JOIN sys.columns AS col
ON col.object_id = tab.object_id
LEFT JOIN sys.types AS t
ON col.user_type_id = t.user_type_id

DECLARE @Iterations INT = 0,
@CurrentIteration INT = 1;

SELECT @Iterations = MAX (Iteration)
FROM #ColumnsToDisplay

WHILE @CurrentIteration <= @Iterations
BEGIN
DECLARE @CurrentTableName VARCHAR(100) = '',
@CurrentColumnName VARCHAR(100) = '',
@DynamicQuery NVARCHAR(1000) = N''
DECLARE @Sample VARCHAR(MAX)

SET @CurrentTableName = '';
SET @DynamicQuery = N'';
SELECT @CurrentTableName = CONCAT (ttq.SchemaName, '.', ttq.table_name),
@CurrentColumnName = ttq.column_name
FROM #ColumnsToDisplay AS ttq
WHERE ttq.Iteration = @CurrentIteration

IF (@CurrentTableName = '')
BEGIN
SET @CurrentIteration += 1

CONTINUE
END

-- SQL Server 2019
-- SET @DynamicQuery = CONCAT (N'
-- SELECT @Sample = STRING_AGG(t.ColumnData,'', '')
-- FROM (
-- SELECT TOP 5 CAST(x.[', @CurrentColumnName, '] AS VARCHAR(MAX)) AS ColumnData
-- FROM ', @CurrentTableName, ' AS x
-- WHERE x.[', @CurrentColumnName, '] IS NOT NULL
-- )t')

-- SQL Server 2016 and lower where Stuff is supported
SET @DynamicQuery = CONCAT (N'
SELECT @Sample = STUFF((SELECT '', ''+ t.ColumnData
FROM (
SELECT TOP 5 CAST(x.[', @CurrentColumnName, '] AS VARCHAR(MAX)) AS ColumnData
FROM ', @CurrentTableName, ' AS x
WHERE x.[', @CurrentColumnName, '] IS NOT NULL
) AS t
FOR XML PATH('''')),1,1,'''')')

EXECUTE sys.sp_executesql @DynamicQuery,
N'@Sample VARCHAR(MAX) OUTPUT',
@Sample = @Sample OUTPUT

UPDATE #ColumnsToDisplay
SET DataSample = @Sample
WHERE Iteration = @CurrentIteration

SET @CurrentIteration += 1
END

SELECT ctd.Iteration,
ctd.SchemaName,
ctd.table_name,
ctd.column_id,
ctd.column_name,
ctd.data_type,
ctd.max_length,
ctd.PrecisionNumber,
ctd.DataSample
FROM #ColumnsToDisplay AS ctd

T-SQL PIVOT data from long form to wide by a date

The traditional cross tab / conditional aggregation version of a pivot() would be like so:

select 
id
, date
, v28 = sum(case when num = 28 then value end)
, v29 = sum(case when num = 29 then value end)
, v30 = sum(case when num = 30 then value end)
from t
group by id, date

pivot() version:

select
Id
, date
, v28
, v29
, v30
from
(select id, date, num = 'v'+convert(varchar(10),num), value
from t) as t
pivot (sum(value) for num in (v28, v29, v30)) pvt

dynamic pivot code generation:

declare @cols nvarchar(max);
declare @sql nvarchar(max);
select @cols = stuff((
select distinct
', ' + 'v'+convert(varchar(10),num)
from t
order by 1
for xml path (''), type).value('.','nvarchar(max)')
,1,2,'')
select @sql = '
select Id, date, ' + @cols + '
from (
select Id, date, num = ''v''+convert(varchar(10),num), value
from t
) as t
pivot (sum([value]) for [num] in (' + @cols + ') ) p'
select @sql
exec(@sql);

generates the following:

select Id, date, v28, v29, v30
from (
select Id, date, num = 'v'+convert(varchar(10),num), value
from t
) as t
pivot (sum([value]) for [num] in (v28, v29, v30) ) p

test them all here: http://rextester.com/ZJS18834

results (ordered by id, date)

+----+---------------------+-----+------+------+
| id | date | v28 | v29 | v30 |
+----+---------------------+-----+------+------+
| 1 | 20.01.2005 00:00:00 | 58 | 197 | 90 |
| 1 | 08.02.2005 00:00:00 | 67 | 210 | 133 |
| 2 | 10.01.2005 00:00:00 | 87 | NULL | 119 |
| 2 | 11.07.2005 00:00:00 | 77 | 174 | NULL |
+----+---------------------+-----+------+------+


Related Topics



Leave a reply



Submit