How to unpivot table with dynamic columns - sql

I'm trying to transpose a table called CHECK_CARD. It looks like this:
_________________________________________________________________
| anc_report_date | RiskSignal | group_company_attr |
|_____________________________|______________|____________________|
| 2019-01-01 00:00:00.0000000 | NoRiskSignal | 4894543 |
| 2016-07-01 00:00:00.0000000 | RiskSignal | 1242151 |
I want it to look like this:
____________________________________________________________________________
| anc_report_date |2019-01-01 00:00:00.0000000|2016-07-01 00:00:00.0000000|
|____________________|___________________________|___________________________|
| RiskSignal | NoRiskSignal | RiskSignal |
| group_company_attr | 1242151 | 1242151 |
The number of rows in anc_report_date is dynamic so what I'm trying to do is:
DECLARE #Column NVARCHAR(MAX)
SET #Column
= 'SELECT cc.anc_report_date
from CHECK_CARD cc
inner join CHECK_CARD chc on cc.group_company_attr=chc.group_company_attr and cc.anc_report_date<=chc.anc_report_date and chc.id=1832307
where cc.status=1'
exec sp_executesql #Column
Here I collect all column names.
DECLARE #Column NVARCHAR(MAX)
SET #Column
= 'SELECT cc.anc_report_date
from CHECK_CARD cc
inner join CHECK_CARD chc on cc.group_company_attr=chc.group_company_attr and cc.anc_report_date<=chc.anc_report_date and chc.id=1832307
where cc.status=1'
DECLARE #Transpose as NVARCHAR(MAX)
set #Transpose
= 'SELECT RiskSignal, group_company_attr
from CHECK_CARD
unpivot
(
kek
for anc_report_date in ('+ #Column +')
) u'
exec sp_executesql #Transpose;
But when I use it all together it returns "Incorrect syntax near the keyword 'SELECT'."
I think this line ('+ #Columns +') is the reason.
Any ideas?
PRINT #Transpose;
[2019-09-19 18:54:42] [S0001] SELECT RiskSignal, group_company_attr
[2019-09-19 18:54:42] from CHECK_CARD
[2019-09-19 18:54:42] unpivot
[2019-09-19 18:54:42] (
[2019-09-19 18:54:42] kek
[2019-09-19 18:54:42] for anc_report_date in (SELECT cc.anc_report_date
[2019-09-19 18:54:42] from CHECK_CARD cc
[2019-09-19 18:54:42] inner join CHECK_CARD chc on cc.group_company_attr=chc.group_company_attr and cc.anc_report_date<=chc.anc_report_date and chc.id=1832307 where cc.status=1)
[2019-09-19 18:54:42] ) u
[2019-09-19 18:54:42] completed in 3 ms

Made it, Thanks all!
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(cc.id)
from CHECK_CARD cc
inner join CHECK_CARD chc
on cc.group_company_attr = chc.group_company_attr and cc.anc_report_date <= chc.anc_report_date and
chc.id = 1832307
where cc.status = 1
group by cc.id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT AttrName,' + #cols + N' from
(
select id, AttrName, value
from CHECK_CARD
cross apply
(
select ''RiskSignal'', cast(RiskSignalas varchar(50)) union all
select ''group_company_attr'', cast(group_company_attr varchar(50))
) c(AttrName, value)
) d
pivot
(
max(value)
for id in (' + #cols + N')
) piv';
exec sp_executesql #query;

Related

How to use table variable in SQL Pivot dynamic query

Error: Must declare the table variable "#Temp_FormData".
Query:
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
DECLARE #Temp_FormData TABLE
(
Id UNIQUEIDENTIFIER,
FormId UNIQUEIDENTIFIER,
ContactId UNIQUEIDENTIFIER,
FieldName NVARCHAR(256),
FieldValue NVARCHAR(MAX),
Created DATETIME
)
;WITH TBL AS(
SELECT fe.Id, fe.FormId, fe.ContactId, fd.FieldName, fd.FieldValue, fe.Created
FROM FormEntries fe (NOLOCK)
LEFT JOIN FieldData fd (NOLOCK) ON fd.FormId = fe.Id
)
INSERT INTO #Temp_FormData
SELECT * FROM TBL
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(FieldName)
FROM #Temp_FormData
GROUP BY FieldName
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = N'SELECT ' + #cols + N' FROM
(
SELECT FieldValue, FieldName
FROM #Temp_FormData /* Error here. Tried enclosing variable in square brackets*/
) x
PIVOT
(
MAX(FieldValue)
FOR FieldName in (' + #cols + N')
) p'
EXEC sp_executesql #query
If I execute select * from #Temp_FormData this is the result:
Id
FormId
ContactId
FieldName
FieldValue
Created
1
100
null
Name
John
2023-01-26
1
100
null
Age
25
2023-01-26
2
200
5001
Name
Peter
2023-01-20
2
200
5001
Age
30
2023-01-20
Expected Output:
Id
FormId
ContactId
Name
Age
Created
1
100
null
John
25
2023-01-26
2
200
5001
Peter
30
2023-01-20

Dynamic Pivot Not displaying right output

I have this table:
Year Month| User| Player | Manager
1996-06 | 1256| 2 | 1
1997-07 | 1243| 5 | 2
and was trying to pivot it so it returns:
| 1997-06|1996-07
User | 1256 | 1243
Player | 2 | 5
Manager| 1 | 2
however I'm not to sure why my script below is returning something different:
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns += N', p.' + QUOTENAME([User])
FROM (SELECT p.[User] FROM dbo.practise AS p
GROUP BY p.[User]) AS x;
SET #sql = N'
SELECT ' + STUFF(#columns, 1, 2, '') + '
FROM
(
SELECT [year month], [User]
FROM dbo.practise AS p
) AS j
PIVOT
(
SUM([User]) FOR [year month] IN ('
+ STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')
+ ')
) AS p;';
PRINT #sql;
EXEC sp_executesql #sql;
It returns:
1256|1243
1 Null|Null
Not sure what I'm doing wrong :/
Your could simplify this a bit
Example
Declare #SQL varchar(max) = '
Select *
From (
Select [Year Month]
,B.*
From practise
Cross Apply (values (''User'' ,cast([User] as varchar(max)))
,(''Player'' ,cast(Player as varchar(max)))
,(''Manager'',cast(Manager as varchar(max)))
) B (Item,Value)
) A
Pivot (max([Value]) For [year month] in (' + Stuff((Select Distinct ','+QuoteName([year month])
From practise
Order By 1
For XML Path('')),1,1,'') + ') ) p
Order by 1 Desc'
Exec(#SQL);
--Print #SQL
Returns
Item 1996-06 1997-07
User 1256 1243
Player 2 5
Manager 1 2
The Generated SQL Looks Like This
Select *
From (
Select [Year Month]
,B.*
From practise
Cross Apply (values ('User' ,cast([User] as varchar(max)))
,('Player' ,cast(Player as varchar(max)))
,('Manager',cast(Manager as varchar(max)))
) B (Item,Value)
) A
Pivot (max([Value]) For [year month] in ([1996-06],[1997-07]) ) p
Order by 1 Desc

Dynamically select distinct current and previous columns from a sql table

I have a temporary table like so
Id |Name |Status | Rate | Method |ModifiedTime |ModifiedBy
-----------------------------------------------------------------------------
1 |Recipe1 | 0 | 30 | xyz | 2016-07-26 14:55:57.977 | A
-------------------------------------------------------------------------------
2 |Recipe1 | 0 | 30 | abc | 2016-07-26 14:56:18.123 | A
--------------------------------------------------------------------------------
3 |Recipe1 | 1 | 30 | xyz | 2016-07-26 14:57:50.180 | b
I would like to select only the changes and wanted to show what the value was previously and what it is currently accompanied by who changed it. The final outcome will be as follows. I am using SQL Server 2014.
Item | Before | After |ModifiedTime | ModifiedBy
-----------------------------------------------------------------------------
Method | xyz | Abc | 2016-07-26 14:56:18.123 | A
-------------------------------------------------------------------------------
Status | 0 | 1 | 2016-07-26 14:57:50.180 | b
--------------------------------------------------------------------------------
Method | Abc | xyz | 2016-07-26 14:57:50.180 | b
I would like to do this dynamically instead of specifying each column name individually as shown in this link
Link
Assuming NAME (Recipe1) is a key
Declare #Table table (Id int,Name varchar(50),Status int,Rate int,Method varchar(50),ModifiedTime DateTime,ModifiedBy varchar(50))
Insert Into #Table values
(1,'Recipe1',0,30,'xyz','2016-07-26 14:55:57.977','A'),
(2,'Recipe1',0,30,'abc','2016-07-26 14:56:18.123','A'),
(3,'Recipe1',1,30,'xyz','2016-07-26 14:57:50.180','b')
Declare #XML xml
Set #XML = (Select * from #Table for XML RAW)
;with cteBase as (
Select ID = r.value('#Id','int')
,Name = r.value('#Name','varchar(150)')
,ModifiedTime = r.value('#ModifiedTime','varchar(150)')
,ModifiedBy = r.value('#ModifiedBy','varchar(150)')
,Item = Attr.value('local-name(.)','varchar(max)')
,Value = Attr.value('.','varchar(max)')
From #XML.nodes('/row') AS A(r)
Cross Apply A.r.nodes('./#*[local-name(.)!="Id"]') AS B(Attr)
)
,cteExt as (Select *,LastValue =Lag(Value) over (Partition By Name,Item Order by ModifiedTime) From cteBase)
Select Name
,Item
,Before=LastValue
,After =Value
,ModifiedTime
,ModifiedBy
From cteExt
Where Value<>LastValue and LastValue is not null
and Item not in ('ModifiedTime','ModifiedBy')
Order By Name,ModifiedTime
Returns
Name Item Before After ModifiedTime ModifiedBy
Recipe1 Method xyz abc 2016-07-26T14:56:18.123 A
Recipe1 Method abc xyz 2016-07-26T14:57:50.180 b
Recipe1 Status 0 1 2016-07-26T14:57:50.180 b
Ok, I adapted my previous answer but on Dynamic SQL. It's a little crazy but it works (using testTable as table name you can change that just replace 'testTable'):
DECLARE #query NVARCHAR(max)
SET #query = 'select item, case item';
SELECT #query = #query + Stuff(( SELECT
' when '''+a.NAME+''' then cast(prev'+a.NAME+' as varchar) '
FROM sys.all_columns a JOIN sys.tables t ON
t.object_id = a.object_id AND t.NAME = 'testTable' AND a.NAME
NOT IN ('id',
'Name', 'ModifiedTime', 'ModifiedBy') FOR xml path('') ), 1, 0,
'');
SET #query = #query + ' end as Before, case item ';
SELECT #query = #query + Stuff(( SELECT
' when '''+a.NAME+''' then cast('+a.NAME+' as varchar) '
FROM sys.all_columns a JOIN sys.tables t ON t.object_id =
a.object_id AND t.NAME = 'testTable' AND a.NAME NOT IN (
'id',
'Name',
'ModifiedTime', 'ModifiedBy') FOR xml path('') ), 1, 1,
'');
SET #query = #query
+ ' end as After, ModifiedTime, ModifiedBy from ( select ';
SELECT #query = #query + Stuff(( SELECT a.NAME +', lag('+ a.NAME +
') over (partition by Name order by id) prev'+a.NAME+', '
FROM
sys.all_columns a JOIN sys.tables t ON t.object_id =
a.object_id
AND t.NAME = 'testTable' AND a.NAME NOT IN ('id', 'Name',
'ModifiedTime', 'ModifiedBy') FOR xml path('') ), 1, 0,
'');
SET #query = #query
+ ' ModifiedBy, ModifiedTime from testTable ) as t1 cross join ('
;
SELECT #query = #query + Stuff(( SELECT ' select '''+ a.NAME +
'''as item union all '
FROM
sys.all_columns a JOIN sys.tables t ON t.object_id =
a.object_id
AND t.NAME =
'testTable' AND a.NAME NOT IN ('id', 'Name',
'ModifiedTime',
'ModifiedBy') FOR xml path('') ), 1, 1, '');
SET #query = LEFT(#query, Len(#query) - 10); --get rid of last union all
SET #query = #query + ' ) items where ';
SELECT #query = #query + Stuff(( SELECT ' or (item = '''+ a.NAME +''' and '+
a.NAME +
' != prev'+ a.NAME +')' FROM sys.all_columns a JOIN
sys.tables
t ON t.object_id = a.object_id AND t.NAME = 'testTable'
AND
a.NAME NOT IN ('id', 'Name', 'ModifiedTime', 'ModifiedBy'
) FOR
xml
path('') ), 1, 3, '');
SET #query = #query + ' order by ModifiedTime';
EXECUTE Sp_executesql
#query

How to pivot the given structure to expected one?

I have queries results as in the image, similarly for 90 date's, so how to group object and make date's as columns and count to respective date's.
Thanks in advance!!
You can do it using a dynamic crosstab:
SQL Fiddle
DECLARE #sql1 VARCHAR(4000) = ''
DECLARE #sql2 VARCHAR(4000) = ''
DECLARE #sql3 VARCHAR(4000) = ''
SELECT #sql1 =
'SELECT
[Object]' + CHAR(10)
SELECT #sql2 = #sql2 +
' , MAX(CASE WHEN [Date] = CAST(''' + CONVERT(VARCHAR(8), [Date], 112) + ''' AS DATE) THEN [count] END) AS ' + QUOTENAME([Date]) + CHAR(10)
FROM(
SELECT DISTINCT [Date] FROM tbl
)t
ORDER BY [Date]
SELECT #sql3 =
'FROM tbl
GROUP BY [Object]
ORDER BY [Object]'
PRINT(#sql1 + #sql2 + #sql3)
EXEC (#sql1 + #sql2 + #sql3)
RESULT
| Object | 2015-01-01 | 2015-01-02 |
|--------|------------|------------|
| 1 | 10 | 34 |
| 2 | 20 | 46 |
| 3 | 130 | 78 |
| 4 | 40 | 89 |
| 5 | 55 | 45 |
This is the output of the PRINT command:
SELECT
[Object]
, MAX(CASE WHEN [Date] = CAST('20150101' AS DATE) THEN [count] END) AS [2015-01-01]
, MAX(CASE WHEN [Date] = CAST('20150102' AS DATE) THEN [count] END) AS [2015-01-02]
FROM tbl
GROUP BY [Object]
ORDER BY [Object]
You can use SQL Server PIVOT relational operator
DECLARE #SQLQuery AS NVARCHAR(MAX)
DECLARE #PivotColumns AS NVARCHAR(MAX)
--Get unique values of pivot column
SELECT #PivotColumns= COALESCE(#PivotColumns + ',','') + QUOTENAME([Date])
FROM (SELECT DISTINCT [Date] FROM [dbo].[PivotExample]) AS PivotExample
--Create the dynamic query with all the values for
--pivot column at runtime
SET #SQLQuery =
N'SELECT ObjectId, ' + #PivotColumns + '
FROM [dbo].[PivotExample]
PIVOT( SUM(COUNT)
FOR [Date] IN (' + #PivotColumns + ')) AS P'
EXEC sp_executesql #SQLQuery

How to pivot table rows into columns with dynamic name

I'm having a very tough time trying to figure out how to do
CID sUSER VALUE
------------------
001 235 10
001 188 20
001 04 5
002 235 11
002 188 12
002 04 13
I would like it displayed as follows
CID 04 188 235
-------------------
001 5 20 10
002 13 12 11
Can someone please show me the Sql code please ?
Lets Assume you have filtered data in #tmpCID; now try below script
Declare #sUser As Varchar(Max)
SELECT #sUser= Case isNull(#sUser,'')
When '' then '['+ Rtrim(sUser) + ']'
Else COALESCE(#sUser + ',[', ',') + Rtrim(sUser)+ ']'
End
FROM (SELECT DISTINCT sUser FROM #tmpCID) U
Declare #Query As Varchar(Max)
Set #Query='Select CID, ' + #sUser + ' From #tmpCID
Pivot (AVG(Value)
For sUser IN ('+ #sUser + ')) As P'
EXEC (#Query)
Try this:
CREATE PROCEDURE YourProcedureName
as
Begin
DECLARE #sUSER AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
select #sUSER = STUFF((SELECT distinct ',' + QUOTENAME(sUSER) FROM testPIVOT
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
SELECT #query = 'WITH PivotData AS
(
SELECT CID, sUSER, VALUE
FROM dbo.YourTableName
)
SELECT CID, '+ #sUSER +'
FROM PivotData
PIVOT(max(VALUE) FOR sUSER IN('+ #sUSER +')) AS P;';
execute(#query);
End
Hope it could help you!
Thanks.