SQL Pivot Table dynamic - sql

I've tried so hard to understand how to create a pivot table in SQL, but I can't manage it!
I have the following columns:
link_id route_section date_1 StartHour AvJT data_source
....... ............. ....... ........... ...... ............
With 600,000 rows of data.
I need them in the following pivot table;
date_1 StartHour as column headings
link_id as the row heading
AvJT as the data
with data_source = '1' as the filter.
PIVOT TABLE
Link_ID
date_1 StartHour 00001a 000002a 000003a 000004a
20/01/2014 8 456 4657 556 46576
21/01/2014 8 511 4725 601 52154
22/01/2014 8 468 4587 458 47585
23/01/2014 8 456 4657 556 46576
24/01/2014 8 456 4657 556 46576
25/01/2014 8 456 4657 556 46576
26/01/2014 8 456 4657 556 46576
I've managed to get the following code, this works but only gives me date_1 as column heading and not StartHour additionally, or with the filter as date_source = '1'.
Use [C1_20132014]
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #ColumnName= ISNULL(#ColumnName + ',','')
+ QUOTENAME(Link_ID)
FROM (SELECT DISTINCT Link_ID FROM C1_May_Routes) AS Link_ID
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
N'SELECT Date_1, ' + #ColumnName + '
FROM C1_May_Routes
PIVOT(SUM(AvJT)
FOR Link_ID IN (' + #ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery
Thanks for any help,
Henry

Here you will select the values in a column to show as column in pivot
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + AvJT + ']', '[' + AvJT + ']')
FROM (SELECT DISTINCT AvJT FROM YourTable) PV
ORDER BY AvJT
Now pivot the query
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT * FROM
(
SELECT date_1, StartHour,AvJT, data_source
FROM YourTable
) x
PIVOT
(
-- Values in each dynamic column
SUM(data_source)
FOR AvJT IN (' + #cols + ')
) p;'
EXEC SP_EXECUTESQL #query
Click here to view result
If you want to do it to where column names are not dynamic, you can do the below query
SELECT DATE_1,STARTHOUR,
MIN(CASE WHEN AvJT='00001a' THEN data_source END) [00001a],
MIN(CASE WHEN AvJT='00002a' THEN data_source END) [00002a],
MIN(CASE WHEN AvJT='00003a' THEN data_source END) [00003a],
MIN(CASE WHEN AvJT='00004a' THEN data_source END) [00004a]
FROM YOURTABLE
GROUP BY DATE_1,STARTHOUR
Click here to view result
EDIT :
I am updating for your updated question.
Declare a variable for filtering data_source
DECLARE #DATASOURCE VARCHAR(20) = '1'
Instead of QUOTENAME, you can use another format to get the columns for pivot
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + Link_ID + ']', '[' + Link_ID + ']')
FROM (SELECT DISTINCT Link_ID FROM C1_May_Routes WHERE data_source=#DATASOURCE) PV
ORDER BY Link_ID
Now pivot
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT * FROM
(
-- We will select the data that has to be shown for pivoting
-- with filtered data_source
SELECT date_1, StartHour,AvJT, Link_ID
FROM C1_May_Routes
WHERE data_source = '+#DATASOURCE+'
) x
PIVOT
(
-- Values in each dynamic column
SUM(AvJT)
-- Select columns from #cols
FOR Link_ID IN (' + #cols + ')
) p;'
EXEC SP_EXECUTESQL #query
Click here to view result

A crosstab would be something like this. FWIW, I would recommend using better column names than 00001a. Give your column names some meaning so they are easier to work with.
with SortedData as
(
SELECT date_1
, StartHour
, AvJT
, data_source
, ROW_NUMBER() over (partition by date_1 order by AvJT) AS RowNum
FROM YourTable
)
select date_1
, StartHour
, MAX(case when RowNum = 1 then AvJT end) as [00001a]
, MAX(case when RowNum = 2 then AvJT end) as [00002a]
, MAX(case when RowNum = 3 then AvJT end) as [00003a]
, MAX(case when RowNum = 4 then AvJT end) as [00004a]
from SortedData

Related

Replace null values with 0 in PIVOT

I tried to convert the (null) values with 0 (zeros) output in PIVOT function but have no success.
Below is the table and the syntax I've tried:
SELECT DISTINCT isnull([DayLoad],0) FROM #Temp1
Data in the table #Temp1:
zone dayB templt cid DayLoad
other 10 other 1 2020-05-28
other 10 other 1 2020-05-29
other 10 other 1 2020-05-30
other 10 other 1 2020-05-31
other 4 other 1 2020-06-02
other 10 other 1 2020-06-02
other 10 other 1 2020-06-01
My request:
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + CONVERT(NVARCHAR, [DayLoad], 106) + ']',
'[' + CONVERT(NVARCHAR, [DayLoad], 106) + ']')
FROM (SELECT DISTINCT [DayLoad] FROM #Temp1) PV
ORDER BY [DayLoad]
DECLARE #query NVARCHAR(MAX)
SET #query = '
SELECT *
into #temptable
FROM
(
SELECT
''''+[zone]+'' '' + ''''+convert(varchar(50),[dayB])+''''+''+'' +'' ''+(case when [templt]=''Прочее'' then '''' else [templt] end)+'''' as [zone/dayB]
,[DayLoad]
,[cid]
,[dayB]
,[zone]
FROM #Temp1
) x
PIVOT
(
sum([cid])
FOR [DayLoad] IN ('+ #cols + ')
) p
select *
from #temptable
order by [zone],[dayB]
drop table #temptable
'
EXEC(#query)
DROP TABLE #Temp1
Please refer below example, when you have nulls and empty string, right option is to go with case statement,
SELECT DISTINCT case when ([DayLoad] is null or [DayLoad] = '') then 0 else [DayLoad] end FROM #Temp1

How to order known values as columns in Dynamic Pivot and new ones to the end?

I am setting up a dynamic pivot where the columns (Department IDs) must be in a set order and any new values that will create a column must be at the end of the table. I set up a Sequence number for the "known" departments and any new departments get the next number in the sequence. I need the Department IDs to be the headings but I need them in the order of the Sequence number.
1) I have pivoted on the Sequence number:
FROM (SELECT DISTINCT [SEQ] FROM #TABLE) AS [SEQ]
ORDER BY [SEQ]
SET #DynamicPivotQuery =
N'SELECT [DATE], ' + #ColumnName + '
FROM #TABLE
PIVOT(SUM([COUNT])
FOR [SEQ] IN (' + #ColumnName + ')) AS PVTTable
ORDER BY [DATE]'
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery
This comes out in the correct order but the Sequence number is the column heading
2) I also have pivoted on the Dept but the columns are in the sequence of the Dept ID:
--Get distinct values of the PIVOT Column
SELECT #ColumnName= ISNULL(#ColumnName + ',','')
+ QUOTENAME([DEPT_ID])
FROM (SELECT DISTINCT [DEPT_ID] FROM #TABLE) AS [DEPT_ID]
ORDER BY [DEPT_ID]
SET #DynamicPivotQuery =
N'SELECT [DATE], ' + #ColumnName + '
FROM #TABLE
PIVOT(SUM([COUNT])
FOR [DEPT_ID] IN (' + #ColumnName + ')) AS PVTTable
ORDER BY [DATE]'
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery
The data/table that is ready to be pivoted is:
SEQ DEPT DATE COUNT
----------------------------------
1 8 1/1/2019 5 (Dept 8 is known Dept)
1 8 1/2/2019 7
2 3 1/1/2019 6 (Dept 3 is known Dept)
2 3 1/2/2019 4
3 1 1/1/2019 7 (Dept 1 is an unknown dept)
3 1 1/2/2019 3
The results I want to see are:
DATE 8 3 1
----------------------------------
1/1/2019 5 6 7
1/2/2019 7 4 3
Or as an image:
You can use the following solution using a dynamic query and PIVOT:
-- declare the variables.
DECLARE #ColumnName NVARCHAR(MAX)
DECLARE #DynamicQuery NVARCHAR(MAX)
-- set the columns (ORDER BY SEQ).
SET #ColumnName = STUFF((
SELECT ',' + QUOTENAME(CONVERT(VARCHAR(10), DEPT))
FROM table_name
GROUP BY SEQ, DEPT
ORDER BY SEQ
FOR XML PATH('')
) , 1 , 1 , '')
-- set the pivot query to get the result.
SET #DynamicQuery = N'
SELECT [DATE], ' + #ColumnName + '
FROM (
SELECT DEPT, DATE, COUNT
FROM table_name
) st PIVOT(
SUM([COUNT])
FOR [DEPT] IN (' + #ColumnName + ')
) pt
ORDER BY [DATE]'
-- execute the dynamic query.
EXEC sp_executesql #DynamicQuery
demo on dbfiddle.uk
In case the order of the columns doesn't matter you can use the following solution:
-- pivot query without specific order of columns.
SELECT DATE, [8], [3], [1]
FROM (
SELECT DEPT, DATE, COUNT
FROM table_name
) st PIVOT (
SUM(COUNT)
FOR DEPT IN ([8], [3], [1])
) pt
ORDER BY [DATE];

Pivot Table with Dynamic

My quest might be answered in somewhere here but I couldn't find. So, sorry if I asked in vain.
I have a Table that's populate automatically with precisely date/time in SQL Server and looks like this:
Cod_Analise| Dat_analise | Nom_usuario| Resultado
-----------+-----------------+------------+-----------
1 | 02/20/2019 14:30| John | 4.5
2 | 02/20/2019 14:31| Carl | 60
3 | 02/21/2019 17:25| Carl | 17
2 | 02/19/2019 06:00| Marcus | 58
1 | 02/20/2019 15:40| Jorge | 5.2
2 | 02/21/2019 22:00| John | 58
and I need something like this:
Dat_Analise | 1 | 2 | 3 | Nom_usuario
------------+---+---+---+------------
02/19/2019 | 0 |58 | 0 | Marcus
02/20/2019 |4.9|60 | 0 | (First or Last one)
I need to do a pivot table based in this table where the Columns are Dat_Analise(date), Nom_operador(who did) and "Cod_Analise"(what did). And the rows are "Resultados"(results).
My problem is, i need to group by time period taking avg results for dynamic number of Cod_analises. But I even did the pivot with dynamic columns but I cannot fit the Group By part inside the pivot table.
I try to use a model that i found here and my procedure is like this:
SELECT
A.[RESULTADO],
A.[DAT_ANALISE],
A.[NOM_USUARIO],
B.[NOM_ANALISE]
into #temporaria
FROM
[BSDB_Processo].[dbo].[RESULTADOS_ANALISES] A,
[BSDB_Processo].[dbo].[ANALISES] B
WHERE
A.COD_PROCESSO = #PROCESSO
AND
A.COD_ANALISE = B.COD_ANALISE
AND
NUM_LOTE =#LOTE
Then:
declare #columnsSrc nvarchar(max) = N''
,#columnsDst nvarchar(max) = N''
,#sql nvarchar(max)
,#KeyColumns nvarchar(max) = N'DAT_ANALISE'
,#compatibility int = (
select top 1 compatibility_level from sys.databases
where name = db_name()
order by Name
);
declare #GroupBy nvarchar(max) =
--case when #compatibility <= 90
-- then case when len(#KeyColumns)=0 then '' else 'group by ' + #KeyColumns +
-- ' with rollup' end
-- else case when len(#KeyColumns)=0 then '' else 'group by rollup ('
-- + #KeyColumns + ')' end
-- end
case when len(#KeyColumns)=0 then '' else 'group by ' + #KeyColumns end;
select
#columnsSrc += nchar(10) + N',' + quotename([NOM_ANALISE])
,#columnsDst += nchar(10) + N',sum(isnull(' + quotename([NOM_ANALISE]) + N',0)) as '
+ quotename([NOM_ANALISE])
from (
select [NOM_ANALISE]
from #temporaria
group by [NOM_ANALISE]
) as x
order by x.[NOM_ANALISE]
And:
set #sql = N'
select ' +
case when len(#KeyColumns)=0 then '' else #KeyColumns + ',' end +
STUFF(#columnsDst, 1, 2, '') + '
INTO ##tabelaAnalises
from (
select' + nchar(10) +
case when len(#KeyColumns)=0 then '' else #KeyColumns + ',' end +
' [NOM_ANALISE],[RESULTADO]
from #temporaria
) as j
pivot (
sum([RESULTADO]) for [NOM_ANALISE] in ('
+ stuff(replace(#columnsSrc, ',p.[', ',['), 2, 1, '')
+ ')
) as p' + nchar(10) +
#GroupBy +
';'
>;
--print #sql;
exec sp_executesql #sql;
select * from ##tabelaAnalises
commit
End
Hope you can help me guys and ,again, sorry if i did something wrong with this post. First time using this
Try to see the below query. Please, see UPDATE section of a pivot with column Nom_usuario.
Sample data:
IF OBJECT_ID('tempdb..#SomeTable') IS NOT NULL DROP TABLE #SomeTable
GO
CREATE TABLE #SomeTable
(
Cod_Analise int,
Dat_analise datetime,
Nom_usuario varchar(50),
Resultado numeric(18,1)
)
INSERT INTO #SomeTable
(
Cod_Analise,
Dat_analise,
Nom_usuario,
Resultado
)
VALUES
( 2, '20190219 06:00', 'Marcus', 58)
, ( 1, '20190220 14:30', 'John', 4.5)
, ( 2, '20190220 14:31', 'Carl', 60)
, ( 1, '20190220 15:40', 'Jorge', 5.2)
, ( 3, '20190221 17:25', 'Carl', 17)
, ( 2, '20190221 22:00', 'John', 58)
A query:
SELECT
pvt.Dat_analise
, pvt.[1]
, pvt.[2]
, pvt.[3]
FROM
(SELECT
CONVERT(date, (t.Dat_analise)) Dat_analise
, t.Cod_Analise
, t.Resultado
FROM #SomeTable t) AS t
PIVOT
(
AVG(T.Resultado)
FOR t.Cod_Analise IN ([1], [2], [3])
) pvt
And dynamic version:
declare #cols nvarchar(max);
declare #sql nvarchar(max);
select #cols = stuff((
select distinct
' , ' + CONCAT('[', CONVERT(varchar(10), t.Cod_Analise), ']')
from #SomeTable t
order by 1
for xml path (''), type).value('.','nvarchar(max)')
,1,2,'')
select #sql = '
select p.Dat_Analise,' + #cols + '
from (
SELECT
CONVERT(date, (t.Dat_analise)) Dat_analise
, t.Cod_Analise
, t.Resultado
FROM #SomeTable t
) as t
pivot (AVG(T.Resultado)
FOR t.Cod_Analise in (' + #cols + ') ) p'
exec(#sql);
OUTPUT:
Dat_analise 1 2 3
2019-02-19 NULL 58.000000 NULL
2019-02-20 4.850000 60.000000 NULL
2019-02-21 NULL 58.000000 17.000000
UPDATE:
Use the following code snippet to show a Nom_usuario:
declare #cols nvarchar(max);
declare #sql nvarchar(max);
select #cols = stuff((
select distinct
' , ' + CONCAT('[', CONVERT(varchar(10), t.Cod_Analise), ']')
from SomeTable t
order by 1
for xml path (''), type).value('.','nvarchar(max)')
,1,2,'')
select #sql = '
select *
from
(
SELECT
CONVERT(date, (t.Dat_analise)) Dat_analise
, t.Cod_Analise
, t.Resultado
, MAX(t.Nom_usuario) OVER (PARTITION BY CONVERT(DATE, (t.Dat_analise))) Nom_usuario
FROM SomeTable t
) as t
pivot (AVG(T.Resultado)
FOR t.Cod_Analise in (' + #cols + ') ) p'
exec(#sql);
OUTPUT:
Dat_analise Nom_usuario 1 2 3
2019-02-21 John NULL 58.000000 17.000000
2019-02-20 Jorge 4.850000 60.000000 NULL
2019-02-19 Marcus NULL 58.000000 NULL

Normal pivot to convert into dynamic or any other

I have table with this kind of data
ID name St_dt points
1 Mohan 2017-07-10 50
1 Mohan 2017-07-07 30
I want result Set like this
Output :
ID name 2017-07-10 2017-07-07 Difference %
1 Mohan 50 30 20 66.7
I have implemented Pivot function and achieved above result
sample Script :
Select
ID,
name,
[2017-07-10],
[2017-07-07],
[Difference] = [2017-07-10] - [2017-07-07],
case when [2017-07-10] > [2017-07-07]
then cast(round (([2017-07-10] - [2017-07-07]) *1. / [2017-07-07] * 100, 2) as decimal(3,1))
else 0
end as [%]
from (
select ID,name,St_dt,points from Table
)T
PIVOT (MAX(points)FOR St_dt IN ([2017-07-10],[2017-07-07]) )PVT
Up to now this is fine but when I'm trying achieve the same in Dynamic Pivot I'm facing the issue at percentage calculation. How i can achieve in Dynamic.
Hope my question is clear
Please check my dynamic query up to Difference calculation unable to achieve percentage calculation in dynamic
Dynamic Script :
DECLARE #cols AS NVARCHAR(MAX)='';
DECLARE #query AS NVARCHAR(MAX)='';
DECLARE #select_cols AS NVARCHAR(MAX)='';
DECLARE #diff_cols varchar(MAX) = '';
SELECT #cols = #cols + QUOTENAME(St_dt) + ',' FROM (select distinct CONVERT(DATE,St_dt)St_dt from #T
) as tmp ORDER BY St_dt desc
SELECT #cols = substring(#cols, 0, len(#cols))
select #cols
Set #diff_Cols = stuff((SELECT '-Max('+Quotename(CONVERT(DATE, St_dt))+') '
FROM #T
group by St_dt
ORDER BY St_dt DESC
FOR xml path('')) ,1,1,'')
select #diff_cols
Select #query = '
Select ID,name,
'+#cols+',
Difference = '+#diff_cols+'
from (
SELECT ID,name,St_dt,points
FROM #T
)T
PIVOT (MAX(Points)FOR St_dt IN ('+#cols+') )PVT
GROUP BY ID,name,'+#cols+'
'
EXEC (#query)
Try this below ,It may helps you
IF OBJECT_ID('tempdb..#TempTable') IS NOT NULL
DROP TABLE #TempTable
Declare #Table TABLE (ID INT,name VARCHAR(50),St_dt DATE,points INT)
INSERT INTO #Table
SELECT 1,'Harini','2017-07-10',50 Union all
SELECT 1,'Harini','2017-07-07',30
SELECT * INTO #TempTable FROM #Table
DECLARE #Sql NVARCHAR(max)
,#SelectSQL NVARCHAR(max)
,#COlumns NVARCHAR(max)
,#Previousday NVARCHAR(max)
,#Difference NVARCHAR(max)
,#CurrentDay NVARCHAR(max)
SELECT #COlumns=STUFF((SELECT ', '+ QUOTENAME(CAST(St_dt AS VARCHAR(10)))FROM #TempTable GROUP BY St_dt ORDER BY St_dt DESC FOR XML PATH('')),1,1,'')
SELECT #SelectSQL = STUFF((
SELECT ', ' + 'ISNULL( MAX( ' + QUOTENAME(CAST(St_dt AS VARCHAR(10))) +' )'+ ',' + '''0''' + ') AS ' + QUOTENAME(CAST(St_dt AS VARCHAR(10)))
FROM #TempTable GROUP BY St_dt ORDER BY ST_DT DESC
FOR XML PATH('')
), 1, 1, '')
SELECT #CurrentDay=SUBSTRING(#COlumns,CHARINDEX(',',#COlumns)+1,LEN(#COlumns)),#Previousday=SUBSTRING(#COlumns,0,CHARINDEX(',',#COlumns))
SET #Difference=#Previousday+' - '+ #CurrentDay
SET #Sql=N'
SELECT ID,name,'+#SelectSQL+', [Difference]='+#Difference+',
CASE WHEN '+#Previousday+' > '+#CurrentDay+' THEN
CAST(ROUND (('+#Difference+') *1./'+#CurrentDay+'* 100, 2) AS DECIMAL(3,1)) ELSE 0 END AS [%]
FROM (
SELECT ID,name,St_dt,points FROM #TempTable
)Src
PIVOT
(
MAX(points) FOR St_dt IN ('+#COlumns+')
)AS PVT
Group by PVT.ID,PVT.name,
PVT.'+#CurrentDay+',PVT.'+#Previousday+''
PRINT #Sql
EXECute(#Sql)
Result
ID name 2017-07-10 2017-07-07 Difference %
------------------------------------------------------
1 Harini 50 30 20 66.7

Select transformation to table

I have data from select query which looks this way:
Id EventName Quantity
A930FF06-B9F2-4D06-A28E-00037E82DDB1 DiscountClick 40
A930FF06-B9F2-4D06-A28E-00037E82DDB1 DiscountLike 1
A930FF06-B9F2-4D06-A28E-00037E82DDB1 DiscountSave 11
A930FF06-B9F2-4D06-A28E-00037E82DDB1 DiscountView 2579
28D64EEB-97FB-45A9-AA4C-00359FF6FF42 DiscountClick 22
28D64EEB-97FB-45A9-AA4C-00359FF6FF42 DiscountSave 1
28D64EEB-97FB-45A9-AA4C-00359FF6FF42 DiscountView 971
I want transform it to table which looks this:
Id DiscountView Discount...
A930FF06-B9F2-4D06-A28E-00037E82DDB1 2579
28D64EEB-97FB-45A9-AA4C-00359FF6FF42 971
How I can do that?
This is the dynamic pivot query for your problem.
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX),
#PivotColumnNames AS NVARCHAR(MAX),
#PivotSelectColumnNames AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #PivotColumnNames= ISNULL(#PivotColumnNames + ',','')
+ QUOTENAME(EventName)
FROM (SELECT DISTINCT EventName FROM test_table) AS Courses
--Get distinct values of the PIVOT Column with isnull
SELECT #PivotSelectColumnNames
= ISNULL(#PivotSelectColumnNames + ',','')
+ 'ISNULL(' + QUOTENAME(EventName) + ', 0) AS '
+ QUOTENAME(EventName)
FROM (SELECT DISTINCT EventName FROM test_table) AS Courses
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
N'SELECT Id, ' + #PivotSelectColumnNames + '
FROM test_table
pivot(sum(quantity) for EventName in (' + #PivotColumnNames + ')) as pvt';
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery
This is the traditional cross tab / conditional aggregation version of a pivot():
select
Id
, DiscountView = sum(case when EventName = 'DiscountView' then Quantity end)
, DiscountSave = sum(case when EventName = 'DiscountSave' then Quantity end)
, DiscountLike = sum(case when EventName = 'DiscountLike' then Quantity end)
from t
group by Id
pivot() version:
select
Id
, DiscountView
, DiscountSave
, DiscountLike
from t
pivot (sum(Quantity) for EventName in (DiscountView, DiscountSave, DiscountLike)) pvt