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
Related
TABLE-A:-
Custno
Name
Route
Phone
1
C1
1
12345
2
C2
1
23456
3
C3
2
34567
4
C4
1
45678
5
C5
1
56789
TABLE-B:-
ODate
Custno
Route
ProductId
qty
2021-04-22
1
1
1
100
2021-04-22
1
1
3
200
2021-04-22
2
1
1
120
Table-C
ProductId
BrandName
1
Brand-1
2
Brand-2
3
Brand-3
EXPECTED RESULT
Phone
CustNo
Name
Brand-1
Brand-2
Brand-3
12345
1
C1
100
200
23456
2
C2
120
45678
4
C4
56789
5
C5
What I tried Using Dynamic Pivot
DECLARE #query AS VARCHAR(MAX)
, #cols_ AS vARCHAR(MAX)
--Making the column list dynamically
select #cols_ = STUFF((SELECT ',' + QUOTENAME(brandname) from [Table-C] order by productid FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)') ,1,1,'')
print #cols_
--preparing PIVOT query dynamically.
SET #query = ' SELECT
pivoted.*
into #Temp_data
FROM
(
select a.phone,a.custno,a.[name],d.BrandName,c.qty from [Table-A] a inner join [Table-B] c on a.custno = c.custno inner join [Table-C] d on c.productid = d.Productid and a.Route='1' and c.odate='2021-04-22'
) AS [p]
PIVOT
(
MIN([P].[qty])
FOR [P].[BrandName] IN (' + #cols_ + ')
) AS pivoted
order by custno;
select *
from #Temp_data [B]
-- GROUP BY [B].[ODate]
drop table #Temp_data
';
EXEC (#query)
You can reconstruct the query
SELECT *
FROM
(
SELECT A.[Phone], A.[CustNo], A.[Name], C.[BrandName], B.[qty]
FROM [Table-A] AS A
LEFT JOIN [Table-B] AS B
ON A.[CustNo] = B.[CustNo]
AND B.[odate] = '2021-04-22'
LEFT JOIN [Table-C] AS C on C.productid = B.Productid
WHERE A.[Route] = 1
) t
PIVOT
(
MIN([qty]) FOR [BrandName] IN ([Brand-1],[Brand-2],[Brand-3])
) AS piv
which contains LEFT JOIN rather than INNER JOIN, and STRING_AGG() function in order to generate the pivoted columns dynamically as in the following code block
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
SET #cols = ( SELECT STRING_AGG(QUOTENAME([BrandName]),',')
FROM (SELECT DISTINCT [BrandName]
FROM [Table-C] ) C );
SET #query =
N'SELECT *
FROM
(
SELECT A.[Phone], A.[CustNo], A.[Name], C.[BrandName], B.[qty]
FROM [Table-A] AS A
LEFT JOIN [Table-B] AS B
ON A.[CustNo] = B.[CustNo]
AND B.[odate] = ''2021-04-22''
LEFT JOIN [Table-C] AS C on C.productid = B.Productid
WHERE A.[Route] = 1
) t
PIVOT
(
MIN([qty]) FOR [BrandName] IN (' + #cols + N')
) AS piv'
EXEC sp_executesql #query;
Demo
This is the table I have:
| Scheme Code | MonthYear | Revenue | Revenue2 |
|-------------|-----------|---------|----------|
| 18VDA | 2018.1 | 100 | 50 |
| 18VDA | 2018.2 | 200 | 100 |
| 18VDA | 2018.3 | 200 | 150 |
and I want to pivot it to like this:
| Scheme Code | 2018.1 A | 2018.2 A | 2018.3 A | 2018.1 B | 2018.2 B | 2018.3 B |
|-------------|----------|----------|----------|----------|----------|----------|
| 18VDA | 100 | 200 | 200 | 50 | 100 | 150 |
How do I do it so that it pivots in MonthYear, but it duplicates it for both Revenue and Revenue2?
Thanks
EDIT: Messed up the output table I was hoping for! I've edited the actual output table I want to see!
EDIT 2:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
Select #cols = STUFF((SELECT ',' + QUOTENAME([MonthYear])
from tableA
group by [MonthYear]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT *
FROM ( SELECT [Scheme Code], MonthYear ,[Revenue]
FROM TableA
) a
PIVOT(sum(Revenue) for MonthYear in (' + #cols + ')
) as RevenueMonth
ORDER BY [Scheme Code]'
execute(#query);
This code I wrote will do it for just one column, and I get the output like this:
| Scheme Code | 2018.1 | 2018.2 | 2018.3 |
|-------------|--------|--------|--------|
| 18VDA | 100 | 200 | 200 |
My suggestion always is to try to write your query as a hard-coded or static version first before diving into dynamic SQL. This let's you get the final result you want with a smaller subset of data and you can verify that you have the logic correct.
I would tackle this by performing an UNPIVOT of the two Revenue columns first, then look at applying the PIVOT function. To UNPIVOT you can use either the UNPIVOT function or you can use CROSS APPLY with a UNION ALL to convert your two Revenue columns into a single column. A static version of the query would be similar to this:
select *
from
(
select
t.[Scheme Code],
new_colname = concat(t.[MonthYear], ' ', r.colname),
r.colvalue
from yourtable t
cross apply
(
select 'A', Revenue union all
select 'B', Revenue2
) r (colname, colvalue)
) d
pivot
(
sum(colvalue)
for new_colname in ([2018.1 A], [2018.2 A], [2018.3 A], [2018.1 B], [2018.2 B], [2018.3 B])
) p;
You'll notice that in the CROSS APPLY I added a column with the A or B that I use to identify either the Revenue or Revenue2 columns. This is then used to create the new column names for the PIVOT.
This should generate the result you want. Now to do this dynamically, you just need to convert the SQL to dynamic code. You can use the following to get the result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
Select #cols = STUFF((SELECT ',' + QUOTENAME(concat([MonthYear], x.col))
from yourtable
cross join (select col = ' A' union all select ' B') x
group by [MonthYear], x.col
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT *
FROM
(
select
t.[Scheme Code],
new_colname = concat(t.[MonthYear], '' '', r.colname),
r.colvalue
from yourtable t
cross apply
(
select ''A'', Revenue union all
select ''B'', Revenue2
) r (colname, colvalue)
) a
PIVOT
(
sum(colvalue) for new_colname in (' + #cols + ')
) as x
ORDER BY [Scheme Code]';
exec sp_executesql #query;
Both of these should generate the same results (dbfiddle demo)
Do it with CASE and dynamic sql.
DECLARE #colsA AS NVARCHAR(MAX),
#colsB AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #colsA = (SELECT ', sum(case [MonthYear] when ''' + [MonthYear] + ''' then Revenue end)' + QUOTENAME([MonthYear] + ' A')
from tableA
group by [MonthYear]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'),
#colsB = (SELECT ', sum(case [MonthYear] when ''' + [MonthYear] + ''' then Revenue2 end)' + QUOTENAME([MonthYear] + ' B')
from tableA
group by [MonthYear]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)');
Set #query = 'select [Scheme Code]' + #colsA + #colsB + ' from TableA group by [Scheme Code] order by [Scheme Code];';
print #query;
I have some tables in a DB as mentioned below.,
**Table1**
+-------+--------------+-------------+
|**ID** |**Name** |**Country** |
+-------+--------------+-------------+
| 1 |Avinash Kumar |India |
| 2 |Sat!sh#S |USA |
| 3 |$32kjs |UK |
| 4 |#$### |AFRICA |
| 5 |Krishnas_has |USA |
+-------+--------------+-------------+
**Table2**
+-------+--------------+-------------+-----------+
|**ID** |**Name1** |**Country1** |**Region1**|
+-------+--------------+-------------+-----------+
| 1 |Avinash Kumar |India |EMEA |
| 2 |Sat!sh#S |USA |ASIA## |
| 3 |$32kjs |UK |EU._A |
| 4 |#$### |AFRICA |HAS&# |
| 5 |Krishnas_has |USA |KALc!! |
+-------+--------------+-------------+-----------+
**Table3**
+-------+--------------+-------------+-----------+-----------+
|**ID** |**Name2** |**Country2** |**Region2**|**State2** |
+-------+--------------+-------------+-----------+-----------+
| 1 |Avinash.Kumar |India$ |EMEA |BANG_& |
| 2 |Sat!sh#S |US!!A |ASIA## |SO$TH |
| 3 |$32kjs |U#K |EU._A |TRUTH |
| 4 |#$### |AFRICA |HAS |HAPPY |
| 5 |Krishnas_has |USA# |KALc!! |!ASDF# |
+-------+--------------+-------------+-----------+-----------+
And one more thing is like I want to pass the table name as variable. Something like this
Declare #Table
Select * from #Table
What ever I pass in #Table the code has to work dynamically
Now how can I find the special character columns if i pass #Table = Table3
For example: My output should be like this when I want to find special characters in Table3
**EXPECTED OUTPUT**
+-------+--------------+-------------+-----------+-----------+
|**ID** |**Name2** |**Country2** |**Region2**|**State2** |
+-------+--------------+-------------+-----------+-----------+
| 1 |Avinash.Kumar |India$ | |BANG_& |
| 2 |Sat!sh#S |US!!A |ASIA## |SO$TH |
| 3 |$32kjs |U#K |EU._A | |
| 4 |#$### | | | |
| 5 |Krishnas_has |USA# |KALc!! |!ASDF# |
+-------+--------------+-------------+-----------+-----------+
Can someone please help me with this?
Thanks in advance!
Have a look at this approach:
CREATE TABLE Test (
ID INT,
Name2 NVARCHAR(100),
Country2 NVARCHAR(100),
Region2 NVARCHAR(100),
State2 NVARCHAR(100)
)
GO
DELETE test
INSERT INTO Test VALUES(1, 'Avinash.Kumar', 'India$', 'EMEA', 'BANG_&'), (2, 'Sat!sh#S', 'US!!A', 'ASIA', 'SO$TH'), (3, 'Test1', 'Test2', 'Test3', 'Test4')
GO
CREATE FUNCTION chkStrg4Chars(#InputString NVARCHAR(4000)) RETURNS NVARCHAR(4000)
AS
BEGIN
DECLARE #RetVal NVARCHAR(4000);
SET #RetVal = CASE WHEN #InputString LIKE '%[^A-Za-z0-9 .]%' THEN #InputString ELSE NULL END;
RETURN #RetVal
END;
GO
CREATE PROCEDURE chkTableColumns4Chars(#InputTable NVARCHAR(500))
AS
BEGIN
DECLARE #ColList NVARCHAR(MAX);
DECLARE #ColListNonC NVARCHAR(MAX);
DECLARE #ColListRes NVARCHAR(MAX);
DECLARE #ColListPiv NVARCHAR(MAX);
SELECT #ColList = (SELECT CASE WHEN t.name IN ('nvarchar','varchar') THEN 'dbo.chkStrg4Chars(' + c.name + ') AS ' + c.name ELSE c.name END + ','
FROM sys.columns c
JOIN sys.types t ON c.system_type_id = t.system_type_id AND c.user_type_id = t.user_type_id
WHERE c.object_id = OBJECT_ID(#InputTable)
ORDER BY c.column_id
FOR XML PATH('')
);
SET #ColList = SUBSTRING(#ColList, 1, LEN(#ColList)-1);
SELECT #ColListRes = (SELECT 'MAX(' + c.name + ') AS ' + c.name + ','
FROM sys.columns c
JOIN sys.types t ON c.system_type_id = t.system_type_id AND c.user_type_id = t.user_type_id
WHERE c.object_id = OBJECT_ID(#InputTable)
AND t.name IN ('nvarchar','varchar')
ORDER BY c.column_id
FOR XML PATH('')
);
SET #ColListRes = SUBSTRING(#ColListRes, 1, LEN(#ColListRes)-1);
SELECT #ColListNonC = (SELECT c.name + ','
FROM sys.columns c
JOIN sys.types t ON c.system_type_id = t.system_type_id AND c.user_type_id = t.user_type_id
WHERE c.object_id = OBJECT_ID(#InputTable)
AND t.name NOT IN ('nvarchar','varchar')
ORDER BY c.column_id
FOR XML PATH('')
);
SELECT #ColListPiv = (SELECT c.name + ','
FROM sys.columns c
JOIN sys.types t ON c.system_type_id = t.system_type_id AND c.user_type_id = t.user_type_id
WHERE c.object_id = OBJECT_ID(#InputTable)
AND t.name IN ('nvarchar','varchar')
ORDER BY c.column_id
FOR XML PATH('')
);
SET #ColListPiv = SUBSTRING(#ColListPiv, 1, LEN(#ColListPiv)-1);
DECLARE #stmt NVARCHAR(MAX) = 'SELECT ' + #ColList + ' INTO #TTest1 FROM ' + #InputTable + ';
DECLARE #SubCol nvarchar(max) = (SELECT ColName + ' + CHAR(39) + ',' + CHAR(39) + ' FROM (SELECT ' + #ColListRes + ' FROM #TTest1) x UNPIVOT (StrVal FOR ColName in (' + #ColListPiv + '))u FOR XML PATH('+CHAR(39)+CHAR(39)+'));
SET #SubCol = SUBSTRING(#SubCol, 1, len(#SubCol)-1)
DECLARE #SubStmt nvarchar(max) = ' + CHAR(39) + 'SELECT + ' + #ColListNonC + char(39) + ' + #SubCol + ' + CHAR(39) + ' FROM #TTest1 WHERE COALESCE( ' + CHAR(39) + ' + #SubCol + '+ CHAR(39) + ') IS NOT NULL' + CHAR(39) + ';
EXEC sp_executesql #SubStmt;'
EXEC sp_executesql #stmt;
END
GO
EXEC chkTableColumns4Chars 'dbo.Test'
As you said if you have lot of columns in your table, then writing the sql query to check each column would be a hectic work. For easiness we can do it by executing dynamic sql query.
Query
declare #sql as varchar(max);
select #sql = stuff((
select ', case when [' + [column_name] + '] like '
+ char(39) + '%[^A-Za-z0-9 ]%' + char(39)
+ ' then [' + [column_name] + '] else '
+ char(39) + char(39) + ' end as [' + [column_name] + '] '
from information_schema.columns
where table_name = 'your_table_name'
and [column_name] <> 'ID'
for xml path('')
)
, 1, 1, ''
);
select #sql = 'select [ID], ' + #sql + ' from [your_table_name];';
exec(#sql);
I have the following lines of SQL code which are part of a stored procedure:
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME([vw_Imported_Files].RF_FileName)
FROM [dbo].[vw_Imported_Files] with(nolock)
WHERE [vw_Imported_Files].RF_GUID_ID = #sGUID
AND [vw_Imported_Files].RF_IsEnabled = 1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query2 = 'SELECT ROUND(AVG(CAST(RD_Axis AS FLOAT)),3) AS RD_Axis
,' + #cols + ' FROM
(SELECT ROW_NUMBER() OVER (PARTITION BY DenseRank ORDER BY (SELECT NULL))
AS RowNumber, rd.* FROM
(SELECT DENSE_RANK() OVER (ORDER BY rd.RD_RF_ID) AS DenseRank
,rd.RD_Axis AS RD_Axis
,rd.DN_Values AS DN_Values
,rd.RF_FileName AS RF_FileName
FROM #TEMP rd
WHERE rd.RD_GUID_ID = ' + #sGUID + '
AND rd.RD_IsEnabled = 1
) rd
) rn
pivot
(
max(DN_Values)
for RF_FileName in (' + #cols + ')
) p
GROUP BY RD_Axis, ' + #cols + ' '
execute (#query2)
Actually, this query (#query2) results as shown in the following table.
---------------------------------------------------------------
| 0 | NULL | NULL | 0,996573652935408|
| 0 | NULL | 1,00053003751428 | NULL |
| 0 | 0,999843071844672 | NULL | NULL |
| 0,052 | NULL | NULL | 0,992999630825293|
| 0,052 | 1,02368347072563 | NULL | NULL |
| 0,053 | NULL | 0,992674427713489 | NULL |
| 0,104 | NULL | NULL | 0,998690236570867|
| 0,104 | NULL | 0,996645964692132 | NULL |
| 0,105 | 0,989815140503533 | NULL | NULL |
----------------------------------------------------------------------------
What I want to do, is to clear all the NULL values by grouping the Average of the RD_Axis values that have the same DenseRank value. In the current instance, the DenseRank increases every 3 rows. So, the outcome that I want has to look like as depicted in the next table.
----------------------------------------------------------------------------
| 0 | 0,999843071844672 | 1,00053003751428 | 0,996573652935408|
| 0,052 | 1,02368347072563 | 0,992674427713489 | 0,992999630825293|
| 0,104 | 0,989815140503533 | 0,996645964692132 | 0,998690236570867|
----------------------------------------------------------------------------
I hope to find my inquiry clear. Could you please help me to figure out how to transform the above SQL code in order for me to get the desirable result?
You might need to create a new column variable that gets the MAX(dynamicColumn) value for your outer select.. that way you can remove the GROUP BY at the end.
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME([vw_Imported_Files].RF_FileName)
FROM [dbo].[vw_Imported_Files] with(nolock)
WHERE [vw_Imported_Files].RF_GUID_ID = #sGUID
AND [vw_Imported_Files].RF_IsEnabled = 1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #maxCols = STUFF((SELECT distinct ', MAX(' + QUOTENAME([vw_Imported_Files].RF_FileName) + ') AS ' + QUOTENAME([vw_Imported_Files].RF_FileName)
FROM [dbo].[vw_Imported_Files] with(nolock)
WHERE [vw_Imported_Files].RF_GUID_ID = #sGUID
AND [vw_Imported_Files].RF_IsEnabled = 1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query2 = '
SELECT ROUND(AVG(CAST(RD_Axis AS FLOAT)),3) AS RD_Axis
,' + #maxCols + '
FROM
(SELECT ROW_NUMBER() OVER (PARTITION BY DenseRank ORDER BY (SELECT NULL))
AS RowNumber, rd.*
FROM (SELECT DENSE_RANK() OVER (ORDER BY rd.RD_RF_ID) AS DenseRank
,rd.RD_Axis AS RD_Axis
,rd.DN_Values AS DN_Values
,rd.RF_FileName AS RF_FileName
FROM #TEMP rd
WHERE rd.RD_GUID_ID = ' + #sGUID + ' AND rd.RD_IsEnabled = 1
) rd
) rn
pivot
(
max(DN_Values)
for RF_FileName in (' + #cols + ')
) p
GROUP BY DenseRank'
execute (#query2)
This can probably be done without using PIVOT and be a lot cleaner by using aggregates with CASE expressions
Actually I got the result that I was looking for with the following code.
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME([vw_Imported_Files].RF_FileName)
FROM [dbo].[vw_Imported_Files] with(nolock)
WHERE [vw_Imported_Files].RF_GUID_ID = #sGUID and [vw_Imported_Files].RF_IsEnabled = 1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #maxCols = STUFF((SELECT distinct ', MAX(' + QUOTENAME([vw_Imported_Files].RF_FileName) + ') AS ' + QUOTENAME([vw_Imported_Files].RF_FileName)
FROM [dbo].[vw_Imported_Files] with(nolock)
WHERE [vw_Imported_Files].RF_GUID_ID = #sGUID AND [vw_Imported_Files].RF_IsEnabled = 1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query2 = '
SELECT ' + #maxCols + '
FROM
(SELECT ROW_NUMBER() OVER (PARTITION BY DenseRank ORDER BY (SELECT NULL))
AS RowNumber, rd.*
FROM (SELECT DENSE_RANK() OVER (ORDER BY rd.RD_RF_ID) AS DenseRank
,rd.RD_Axis AS RD_Axis
,rd.DN_Values AS DN_Values
,rd.RF_FileName AS RF_FileName
FROM #TEMP rd
WHERE rd.RD_GUID_ID = ' + #sGUID + ' AND rd.RD_IsEnabled = 1
) rd
) rn
pivot
(
max(DN_Values)
for RF_FileName in (' + #cols + ')
) p
GROUP BY RowNumber ORDER BY RowNumber'
execute (#query2)
At first I generated the outcome that I wanted (#query2) and then I got the average of the RD_Axis values with another query (both ordered by Rownumber asc). Then I linked them with a UNION ALL statement. Btw, thanks for the fresh perspective on my issue because I had been stuck for hours.
I need to format and extract some data from a database. While I can extract the data successfully I am struggling with the jagged nature of it.
What I have is the following:
create table temp
(
QuestionID INT,
AnswerID INT,
AnswerValue NVARCHAR(50)
)
insert into temp values (1, 1, 'Ans C')
insert into temp values (1, 2, 'Ans B')
insert into temp values (1, 3, 'Ans A')
insert into temp values (2, 4, 'Ans D')
insert into temp values (2, 5, 'Ans E')
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.AnswerID)
FROM temp c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT QuestionID, ' + #cols + ' from
(
select QuestionID
, AnswerValue
, AnswerID
from temp
) x
pivot
(
max(AnswerValue)
for AnswerID in (' + #cols + ')
) p '
execute(#query)
drop table temp
Executed this produces
+------------+-------+-------+-------+-------+-------+
| QuestionID | 1 | 2 | 3 | 4 | 5 |
+------------+-------+-------+-------+-------+-------+
| 1 | Ans C | Ans B | Ans A | NULL | NULL |
| 2 | NULL | NULL | NULL | Ans D | Ans E |
+------------+-------+-------+-------+-------+-------+
I just need to format it like this
+------------+-------+-------+-------+
| QuestionID | Q1 | Q2 | Q3 |
+------------+-------+-------+-------+
| 1 | Ans C | Ans B | Ans A |
| 2 | NULL | Ans D | Ans E |
+------------+-------+-------+-------+
Note due to restrictions this needs to be done in SQL rather than an advanced language such as c#.
A few things are wrong with the code. First, you are creating your column list using the AnswerID so the data is being split across multiple columns instead of the Answer for each question.
In order to fix this, you'll want to use a windowing function like row_number() to create a sequence for each question/answer combination.
When creating your dynamic columns change the code to be:
SET #cols = STUFF((SELECT ',' + QUOTENAME('Q'+cast(rn as varchar(10)))
FROM
(
SELECT rn = row_number() over(partition by QuestionID
order by AnswerID)
FROM temp
) c
group by rn
order by rn
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
This will use row_number() and will create the column names based on the QuestionID. Then you'll include the row_number() in your subquery making your code:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SET #cols = STUFF((SELECT ',' + QUOTENAME('Q'+cast(rn as varchar(10)))
FROM
(
SELECT rn = row_number() over(partition by QuestionID
order by AnswerID)
FROM temp
) c
group by rn
order by rn
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT QuestionID, ' + #cols + '
from
(
select QuestionID
, AnswerValue
, col = ''Q''+ cast(row_number() over(partition by QuestionID
order by AnswerID) as varchar(10))
from temp
) x
pivot
(
max(AnswerValue)
for col in (' + #cols + ')
) p '
exec sp_executesql #query;
See SQL Fiddle with Demo. This gives a result:
| QUESTIONID | Q1 | Q2 | Q3 |
|------------|-------|-------|--------|
| 1 | Ans C | Ans B | Ans A |
| 2 | Ans D | Ans E | (null) |
You can use this part of code:
SELECT 'A' + CAST(ROW_NUMBER() OVER(PARTITION BY QuestionID ORDER BY Answer) AS VARCHAR(10)) AS cName
FROM tblAnswers
in order to generate the column names required. The above gives you sth like:
cName
-----
A1
A2
A3
A1
A2
You can subsequently use the above in your dynamic pivot to obtain the desired result:
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(a.cName)
FROM (
SELECT 'A' + CAST(ROW_NUMBER() OVER(PARTITION BY QuestionID ORDER BY Answer) AS VARCHAR(10)) AS cName
FROM tblAnswers
) a
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'),1,1,'')
set #query = 'SELECT Question, ' + #cols + ' ' +
'FROM (
SELECT q.Question, a.Answer,
''A'' + CAST(ROW_NUMBER() OVER(PARTITION BY a.QuestionID ORDER BY Answer) AS VARCHAR(10)) AS cName
FROM tblAnswers AS a
INNER JOIN tblQuestions AS q ON a.QuestionID = q.QuestionID
) t
PIVOT
(
MAX(t.Answer)
FOR cName in (' + #cols + ')
) Pvt '
execute(#query)
Output from above looks like:
Question A1 A2 A3
-----------------------------------
Q1 Answer1 Answer2 Answer3
Q2 Answer4 Answer5 NULL
SQL Fiddle demo here