Pivot Table with Dynamic - sql

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

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

Pivot table data

I have following data
NAME | RIGHTS |
Steven | add |
Steven | update |
Steven | delete |
Mark | update |
Mark | delete |
Joseph | don’t have Rights |
Spike | add |
Spike | update |
Spike | delete |
And this data, I want to manipulate as
NAMEs | don’t have Rights | add| update | delete |
Steven | 0 |1|1|1|
Mark |0|0|1|1|
Joseph |1|0|0|0|
Spike |0|1|1|1|
Note that I have no idea of types of rights, it can be above 100
**Using pivot to get that result :**
CREATE TABLE #details(Id INT,NAME VARCHAR(100), RIGHTS VARCHAR(100))
INSERT INTO #details( Id ,NAME , RIGHTS )
SELECT 1,'Steven','add' UNION ALL
SELECT 1,'Steven','update' UNION ALL
SELECT 1,'Steven','delete' UNION ALL
SELECT 1,'Mark','update' UNION ALL
SELECT 1,'Mark','delete' UNION ALL
SELECT 1,'Joseph','don’t have Rights' UNION ALL
SELECT 1,'Spike','add' UNION ALL
SELECT 1,'Spike','update' UNION ALL
SELECT 1,'Spike','delete'
SELECT *
FROM
(
SELECT NAME , RIGHTS ,Id
FROM #details
)A
PIVOT
(
MAX(Id) FOR RIGHTS IN ([don’t have Rights],[add],[update],[delete])
)pvt
**For Dynamic pivot use below query :**
DECLARE #DynamicCol VARCHAR(MAX) = '',#DynamicPvt VARCHAR(MAX) = ''
SELECT #DynamicCol =
(
SELECT STUFF ((SELECT DISTINCT ',' + '[' + RTRIM(RIGHTS) + ']' FROM
#details FOR XML PATH('')),1,1,'')
)
SET #DynamicPvt = 'SELECT *
FROM
(
SELECT NAME , RIGHTS ,Id
FROM #details
)A
PIVOT
(
MAX(Id) FOR RIGHTS IN ('+#DynamicCol+')
)pvt'
EXEC (#DynamicPvt)
I got the answer
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',[' + Rights +']'
from MyTableName
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT Name,' + #cols + ' from
(
SELECT Name, Rights,1 as xCount FROM MyTableName
) x
pivot
(
count(xCount)
for Rights in (' + #cols + ')
) p '
execute(#query);
Check this.
select NAME,
case when [don’t have Rights] is not null then 1 else 0 end as [don’t have Rights] ,
case when [add] is not null then 1 else 0 end as [add] ,
case when [update] is not null then 1 else 0 end as [update] ,
case when [delete] is not null then 1 else 0 end as [delete]
from
(
select *
from #YourTAble
pivot
(
max(RIGHTS) for RIGHTS in ([don’t have Rights],[delete],[update],[add])
)a
)b
Output :
Declare #Str varchar(max)
, #FinalStr varchar(max)
Set #Str=''
Select #Str = #Str + ',[' + Rights+']'
FROM (Select Distinct Rights FROM F2) A
Set #Str= Substring(#str,2,LEN(#Str))
SET #FinalStr =
'Select Name, ' + #Str +
'FROM F2
PIVOT ( Count(Rights)For Rights in ( ' + #Str + ')) As A'
Exec (#FinalStr)
This is will give the desire result and it's dynamic too. You can have number of rights.

How to pivot rows into colums dynamically SQL Server

I have a request which returns something like this:
--------------------------
Tool | Week | Value
--------------------------
Test | 20 | 3
Sense | 20 | 2
Test | 19 | 2
And I want my input to look like this:
-------------------------
Tool | W20 | W19
-------------------------
Test | 3 | 2
Sense | 2 | null
Basically, for every week I need to have a new column. The number of week and of tools is dynamic.
I have tried many things but nothing worked. Anybody have a solution ?
Try this
CREATE table #tst (
Tool varchar(50), [Week] int, Value int
)
insert #tst
values
('Test', 20, 3),
('Sense', 20,2),
('Test', 19, 2)
Here is the Dynamic Query:
DECLARE #col nvarchar(max), #query NVARCHAR(MAX)
SELECT #col = STUFF((SELECT DISTINCT ',' + QUOTENAME('W' + CAST([Week] as VARCHAR))
from #tst
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = '
SELECT *
FROM (
SELECT Tool,
Value,
''W'' + CAST([Week] as VARCHAR) AS WeekNo
FROM #tst
) t
PIVOT
(
MAX(t.Value)
FOR WeekNo IN (' + #col + ')
) pv
ORDER by Tool'
EXEC (#query)
Result
Tool W20 W19
=================
Sense 2 NULL
Test 3 2
IF OBJECT_ID('tempdb..#temp') IS NOT NULL
DROP TABLE #temp
CREATE TABLE #temp
( Tool varchar(5), Week int, Value int)
;
INSERT INTO #temp
( Tool , Week , Value )
VALUES
('Test', 20, 3),
('Sense', 20, 2),
('Test', 19, 2)
;
DECLARE #statement NVARCHAR(max)
,#columns NVARCHAR(max),
#col NVARCHAR(max)
SELECT #columns = ISNULL(#columns + ', ', '') + N'[' +'w'+ tbl.[Week] + ']'
FROM (
SELECT DISTINCT CAST([Week] AS VARCHAR)[Week]
FROM #temp
) AS tbl
SELECT #statement = 'SELECT *
FROM
(
SELECT
Tool , ''w''+ CAST(Week AS VARCHAR) week , Value
FROM
#Temp
) src
PIVOT(MAX(Value)for Week in (' + #columns + ')) as pvt
'
EXEC sp_executesql #statement = #statement
This is how I would do it ... If I understood your question correctly
if object_id('tempdb..#InputTool') is not null drop table #InputTool
create table #InputTool (Tool nvarchar(10), [20] int, [19] int)
insert into #InputTool (Tool, [20], [19])
values
('Test', 3, 2),
('Sense', 2, null)
declare #cols nvarchar(max)
select #cols = STUFF((SELECT ',' + QUOTENAME(name)
from tempdb.sys.columns
where object_id = object_id('tempdb..#InputTool')
and Column_id > 1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
declare #sqlquery nvarchar(max) =
'select Tool, Weeks, Value from (
select * from #InputTool
) as it
UNPIVOT
(
Value FOR Weeks IN (' + #cols + ')
) AS Weeks
order by Weeks desc'
execute (#sqlquery);
Give it a shot and let me know if it worked

How to provide custom name to column in pivoting

I have a table like this:
id unit
1 mm
2 cm
3 kg
When I perform pivot operation on this, I am getting result as follows:
1 2 3
mm cm kg
Is it possible to get custom column names here, something like this:
d1 d2 d3
mm cm kg
I am using Pivot for this as:
IF OBJECT_ID('tempdb..#t') IS NOT NULL
DROP TABLE #t
GO
CREATE table #t
(id varchar(max),unit varchar(max))
insert into #t (id,unit)values
(1,'kg'),
(2,'cm'),
(3,'mm'),
(4,'m')
DECLARE #statement NVARCHAR(max)
,#columns NVARCHAR(max)
SELECT #columns = ISNULL(#columns + ',', '') + N'[' + cast(tbl.id as varchar(max)) + ']'
FROM (
SELECT DISTINCT id
FROM #t
) AS tbl
SELECT #statement = 'select *
INTO ##temp
from (
SELECT id,[unit]
FROM #t
) as s
PIVOT
(max(unit) FOR id in(' + #columns + ')) as pvt
'
EXEC sp_executesql #statement = #statement
SELECT * FROM ##temp
DROP TABLE #t
DROP TABLE ##temp
Is it possible?
Thanks
IF OBJECT_ID('tempdb..#t') IS NOT NULL
DROP TABLE #t
GO
CREATE TABLE #t (
id VARCHAR(10),
unit VARCHAR(100)
)
INSERT INTO #t (id, unit)
VALUES
('1', 'kg'),
('2', 'cm'),
('3', 'mm'),
('4', 'mm')
DECLARE #SQL NVARCHAR(MAX), #columns NVARCHAR(MAX)
SELECT #columns = STUFF((
SELECT ',[D' + id + ']'
FROM #t
FOR XML PATH('')), 1, 1, '')
SELECT #SQL = '
SELECT *
FROM (
SELECT [unit], col = N''D'' + id
FROM #t
) s
PIVOT (MAX(unit) FOR col IN (' + #columns + ')) p'
EXEC sys.sp_executesql #SQL
Just add a prefix to your ID. Example
SELECT #statement = 'select * INTO ##temp from
( SELECT [id] = ''d''+id,[unit] FROM #t ) as s
PIVOT
(max(unit) FOR id in(' + #columns + ')) as pvt '
Also it's terrible practice to use global temp tables! Especially one named ##temp
You can use a CASE expression with a dynamic sql query.
CREATE TABLE #t
(
id INT,
unit VARCHAR(2)
);
INSERT INTO #t VALUES
(1,'mm'),
(2,'cm'),
(3,'kg');
DECLARE #query AS VARCHAR(MAX);
SELECT #query = 'SELECT ' +
STUFF
(
(
SELECT DISTINCT ',MAX(CASE WHEN id = '+ CAST(id AS VARCHAR(10))
+ ' THEN unit END) AS d' + CAST(id AS VARCHAR(10))
FROM #t
FOR XML PATH('')
),
1,1,'');
SELECT #query += ' FROM #t;';
EXECUTE(#query);
Result
+----+----+----+
| d1 | d2 | d3 |
+----+----+----+
| mm | cm | kg |
+----+----+----+
SELECT #statement = 'select * INTO ##temp from ( SELECT ''d''+id AS [id],[unit] FROM #t ) as s PIVOT (max(unit) FOR id in(' + #columns + ')) as pvt '

Flattening a Single Table

I am looking for a very basic flattening. I have a table in the database which provides each field for the employee in a different row.
EX
EMPLOYEE GROUP_NAME
81 BNEEO55
81 BNELLIG
81 LPKAPE
81 HRFT
90 BNRETINV
....
I would like to create a view which reports the data as:
EMPLOYEE Group1 Group2 Group3 Group4 Group5
81 BNEEO55 BNELLIG LPKAPE HRFT NULL
90 NULL NULL NULL NULL BNRETINV
OR
EMPLOYEE BNEEO55 BNELLIG LPKAPE HRFT BNRETINV
81 YES YES YES YES NO
90 NO NO NO NO YES
You need to PIVOT the data, if you are using SQL-Server 2008 or later you can use the PIVOT function:
CREATE TABLE #T (Employee INT, Group_name VARCHAR(50))
INSERT #T VALUES (81, 'BNEEO55'), (81, 'BNELLIG'), (81, 'LPKAPE'), (81, 'HRFT'), (90, 'BNRETINV')
SELECT Employee,
COALESCE([BNEEO55],'No') AS [BNEEO55],
COALESCE([BNELLIG],'No') AS [BNELLIG],
COALESCE([BNRETINV],'No') AS [BNRETINV],
COALESCE([HRFT],'No') AS [HRFT],
COALESCE([LPKAPE],'No') AS [LPKAPE]
FROM ( SELECT *, 'Yes' [Data]
FROM #T
) d
PIVOT
( MAX(Data)
FOR Group_Name IN ([BNEEO55], [BNELLIG], [BNRETINV], [HRFT], [LPKAPE])
) pvt
But it sounds like you need to do it dynamically if your data is changing. Something like the following would work:
DECLARE #SQL NVARCHAR(MAX) = '',
#Cols NVARCHAR(MAX) = ''
SELECT #SQL = #SQL + ',' + QUOTENAME(Group_Name),
#Cols = #Cols + ',COALESCE(' + QUOTENAME(Group_Name) + ',''No'') AS ' + QUOTENAME(Group_Name)
FROM ( SELECT DISTINCT Group_Name
FROM #T
) T
SET #SQL = '
SELECT Employee, ' + STUFF(#Cols, 1, 1, '') +
'FROM ( SELECT *, ''Yes'' [Data]
FROM #T
) d
PIVOT
( MAX(Data)
FOR Group_Name IN (' + STUFF(#SQL, 1, 1, '') + ')
) pvt'
EXECUTE SP_EXECUTESQL #SQL
This builds up the same query as the first one, but means that when a new group name is added the query doesn't have to be altered.
EDIT
To create and call this as a stored procedure:
CREATE TABLE T (Employee INT, Group_name VARCHAR(50))
INSERT T VALUES (81, 'BNEEO55'), (81, 'BNELLIG'), (81, 'LPKAPE'), (81, 'HRFT'), (90, 'BNRETINV')
GO
CREATE PROCEDURE PivotT
AS
DECLARE #SQL NVARCHAR(MAX) = '',
#Cols NVARCHAR(MAX) = ''
SELECT #SQL = #SQL + ',' + QUOTENAME(Group_Name),
#Cols = #Cols + ',COALESCE(' + QUOTENAME(Group_Name) + ',''No'') AS ' + QUOTENAME(Group_Name)
FROM ( SELECT DISTINCT Group_Name
FROM T
) T
SET #SQL = '
SELECT Employee, ' + STUFF(#Cols, 1, 1, '') +
'FROM ( SELECT *, ''Yes'' [Data]
FROM T
) d
PIVOT
( MAX(Data)
FOR Group_Name IN (' + STUFF(#SQL, 1, 1, '') + ')
) pvt'
EXECUTE SP_EXECUTESQL #SQL
GO
EXECUTE PivotT