Format Jagged data gained from dynamic pivot - sql

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

Related

How to transform rows into column in SQL Server?

Using MS SQL Server and I have below table:
+-----------+------------------+------------------+---------------------+-------------------------+
| SrNo | ApprCode | ApprName | ApprStatus | ApprDate |
+-----------+------------------+------------------+---------------------+-------------------------+
| SR_176 | X001 | James | APR | 2019-10-03 |
| SR_176 | X002 | Sam | APR | 2019-10-03 |
+-----------+------------------+------------------+---------------------+-------------------------+
Tried with PIVOT but its showing james and X001 as a column heading:
Expected Result:
+-----------+-------------- +---------------+---------------------+-------------------------+------------------+
SrNo | ApprCode_1 | ApprName_1 | ApprDate_2 ApprCode_2 ApprName_2 ApprDate_2
+-----------+---------------+---------------+---------------------+-------------------------+------------------+
SR_176 X001 James 2019-10-03 X002 Sam 2019-10-03
+-----------+---------------+---------------+---------------------+---- ---------------------+------------------+
Query to generate data:
CREATE TABLE #Temp
(
SrNo NVARCHAR(200),
ApprCode NVARCHAR(200),
ApprName NVARCHAR(200),
ApprDate Date
)
INSERT INTO #Temp VALUES ('SR_176','X001','James', '2019-10-03')
INSERT INTO #Temp VALUES ('SR_176','X002','Sam', '2019-10-03')
Query that I tried:
declare #sql nvarchar(max)
declare #name_concat nvarchar(max)
declare #name1_concat nvarchar(max)
declare #select_aggs nvarchar(max)
select #name_concat = STUFF((select distinct ',' + quotename(ApprCode) from #Temp order by 1 for xml path('')), 1, 1, '')
select #name1_concat = STUFF((select distinct ',' + quotename(ApprName) from #Temp order by 1 for xml path('')), 1, 1, '')
select #sql = '
;with cte2 as
(
SELECT SrNo,' + #name_concat + ',' + #name1_concat + '
FROM #Temp
PIVOT(MAX(ApprCode)
FOR ApprCode IN (' + #name_concat + ')) AS PVTTable PIVOT
(
MAX(ApprName)
FOR ApprName IN (' + #name1_concat + ')) AS PVTTable1
)
select * from cte2
'
exec sp_executesql #sql
In fact, This is not a SQL Pivot situation and the sample data for question is not enough to test it completely but you can find the main idea :
Select
SrNo,
MAX(IIF(ApprCode = 'X001', ApprCode, null)) as ApprCode_1,
MAX(IIF(ApprCode = 'X001', ApprName, null)) as ApprName_1,
MAX(IIF(ApprCode = 'X001', ApprDate, null)) as ApprDate_1,
MAX(IIF(ApprCode = 'X002', ApprCode, null)) as ApprCode_2,
MAX(IIF(ApprCode = 'X002', ApprName, null)) as ApprName_2,
MAX(IIF(ApprCode = 'X002', ApprDate, null)) as ApprDate_2
From #Temp
Group by SrNo
This code works if ApprCode was the key to make two separate columns & SrNo is for the group by between rows.
I solved it by using ROW_NUMBER() and Case Expression:
Here is the query:
SELECT *,
Row_number()
OVER(
partition BY srno
ORDER BY apprdate) AS RN
INTO #temptable
FROM #temp
SELECT srno,
CASE rn
WHEN 1 THEN Max(apprname)
END AS [1 Approver],
CASE rn
WHEN 2 THEN Max(apprname)
END AS [2 Approver],
CASE rn
WHEN 1 THEN Max(apprcode)
END AS [1 ApproverCode],
CASE rn
WHEN 2 THEN Max(apprcode)
END AS [2 ApproverCode],
CASE rn
WHEN 1 THEN Max(apprdate)
END AS [1 Date],
CASE rn
WHEN 2 THEN Max(apprdate)
END AS [2 Date]
INTO #james
FROM #temptable
GROUP BY srno,
rn
SELECT srno,
Max([1 approver]) AS ApproverName_1,
Max([1 approvercode]) AS ApproverCode_1,
Max([1 date]) AS ApproverDate_1,
Max([2 approver]) AS ApproverName_2,
Max([2 approvercode]) AS ApproverCode_2,
Max([2 date]) AS ApproverDate_2
FROM #james
GROUP BY srno

SQL How to pivot two columns of data into different columns?

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;

How to group mean values of a column after pivoting in SQL Server

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.

SQL Transpose table - sqlserver [duplicate]

This question already has answers here:
Efficiently convert rows to columns in sql server
(5 answers)
Closed 8 years ago.
i have the following table
create table mytab (
mID int primary key,
pname varchar(100) not null,
pvalue varchar(100) not null
)
example data looks like
mID |pname |pvalue
-----------------------
1 |AAR | 2.3
1 |AAM | 1.2
1 |GXX | 5
2 |AAR | 5.4
2 |AAM | 3.0
3 |AAR | 0.2
I want to flip the table so that i get
mID | AAR | AAM | GXX|
---------------------------------
1 | 2.3 | 1.2 | 5|
2 | 5.4 | 3.0 | 0|
3 | 0.2 | 0 | 0
Is this somehow possible and if so, is there a way to create a dynamic query because there are lots of these pname pvalue pairs
Write Dynamic Pivot Query as:
DECLARE #cols AS NVARCHAR(MAX)
DECLARE #query AS NVARCHAR(MAX)
DECLARE #colsFinal AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' +
QUOTENAME(pname)
FROM mytab
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '')
select #colsFinal = STUFF((SELECT distinct ',' +
'ISNULL('+QUOTENAME(pname)+',0) AS '+ QUOTENAME(pname)
FROM mytab
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '')
--Edited query to replace null with 0 in Final result set.
SELECT #query = 'SELECT mID, '+#colsFinal +'
FROM mytab
PIVOT
(
MAX(pvalue)
FOR pname IN(' + #cols + ')) AS p;'
exec sp_executesql #query
Check demo here..
Use pivot as following format:
select *
From (select *
From mytab)p
PIVOT(SUM(pvalue) FOR pname IN ([AAR],[AAM],[GXX]))pvt
In order to dynamic PIVOT use flowing reference:
Dynamic PIVOT Sample1
Dynamic PIVOT Sample2
Declare #t table (mID INT, pname VARCHAR(10), pvalue FLOAT)
INSERT INTO #t (mID,pname,pvalue)values (1,'AAR',2.3)
INSERT INTO #t (mID,pname,pvalue)values (1,'AAM', 1.2)
INSERT INTO #t (mID,pname,pvalue)values (1,'GXX', 5)
INSERT INTO #t (mID,pname,pvalue)values (2,'AAR', 5.4)
INSERT INTO #t (mID,pname,pvalue)values (2,'AAM', 0.3)
INSERT INTO #t (mID,pname,pvalue)values (3,'AAR', 0.2)
select mid,
CASE WHEN [AAR]IS NOT NULL THEN [AAR] ELSE ISNULL([AAR],0)END [AAR],
CASE WHEN [AAM]IS NOT NULL THEN [AAM] ELSE ISNULL([AAM],0)END [AAM],
CASE WHEN [GXX]IS NOT NULL THEN [GXX] ELSE ISNULL([GXX],0)END [GXX]
from
(
select mID, pvalue,pname
from #t
) d
pivot
(
max(pvalue)
for pname in ( [AAR], [AAM], [GXX])
) piv;

Dynamic Pivot (row to columns)

I have a Table1:
ID Instance Name Size Tech
1 0 D1 123 ABC
1 1 D2 234 CDV
2 2 D3 234 CDV
2 3 D4 345 SDF
I need the resultset using Dynamic PIVOT to look like along with the headers:
ID | Instance0_Name | Instance0_Size | Instance0_Tech | Instance1_Name | Instance1_Size | Instance1_tech
1 | D1 | 123 | ABC | D2 | 234 | CDV
Any help would be appreciated. using Sql Server 2008.
Sorry for the earlier post.
Your desired output is not exactly clear, but you can use the both the UNPIVOT and PIVOT function to get the result
If you know the number of columns, then you can hard code the values:
select *
from
(
select id,
'Instance'+cast(instance as varchar(10))+'_'+col col,
value
from
(
select id,
Instance,
Name,
cast(Size as varchar(50)) Size,
Tech
from yourtable
) x
unpivot
(
value
for col in (Name, Size, Tech)
) u
) x1
pivot
(
max(value)
for col in
([Instance0_Name], [Instance0_Size], [Instance0_Tech],
[Instance1_Name], [Instance1_Size], [Instance1_Tech],
[Instance2_Name], [Instance2_Size], [Instance2_Tech],
[Instance3_Name], [Instance3_Size], [Instance3_Tech])
) p
See SQL Fiddle with Demo
Then if you have an unknown number of values, you can use dynamic sql:
DECLARE #query AS NVARCHAR(MAX),
#colsPivot as NVARCHAR(MAX)
select #colsPivot = STUFF((SELECT ','
+ quotename('Instance'+ cast(instance as varchar(10))+'_'+c.name)
from yourtable t
cross apply sys.columns as C
where C.object_id = object_id('yourtable') and
C.name not in ('id', 'instance')
group by t.instance, c.name
order by t.instance
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'select *
from
(
select id,
''Instance''+cast(instance as varchar(10))+''_''+col col,
value
from
(
select id,
Instance,
Name,
cast(Size as varchar(50)) Size,
Tech
from yourtable
) x
unpivot
(
value
for col in (Name, Size, Tech)
) u
) x1
pivot
(
max(value)
for col in ('+ #colspivot +')
) p'
exec(#query)
See SQL Fiddle with Demo
If the result is not correct, then please edit your OP and post the result that you expect from both of the Ids you provided.