sql server group columns as rows (pivot?) - sql

I've got this result data in SQL Server 2008 R2
id Size Acted Sum Avg1 Avg2 A1 A2 A3
1 3921 39 690 17.69 0.18 NULL NULL NULL
40 11979 301 5944.26 19.75 0.5 10000.00 2000.00 1000.00
41 11714 289 5060 17.51 0.43 10000.00 3000.00 2000.00
42 11599 265 4107.98 15.5 0.35 10000.00 5000.00 500.00
And I would like to move the columns into rows according to the id so I will recieve this result:
id1 id40 id41 id42
1 40 41 42
3921 11979 11714 11599
39 301 289 265
690 5944 5060 4107
17.69 19.75 17.51 15.5
0.18 0.5 0.43 0.35
10000.00 2000.00 1000.00
10000.00 3000.00 2000.00
10000.00 5000.00 500.00
Is there a way to do that?
I tried pivot but as far as I tried I could only transform 1 column and not many as needed in this case.

In order to get this result, you will want to first unpivot the data from the columns to rows, and then apply the PIVOT function.
Since you are using SQL Server 2008, you can use CROSS APPLY and VALUES to unpivot the data. This takes the values from your numerous columns and converts them to rows:
select 'id'+cast(t.id as varchar(10)) p_id,
c.col,
c.value,
c.sort_order
from yourtable t
cross apply
(
values
(1, 'id', id),
(2, 'size', size),
(3, 'acted', acted),
(4, 'sum', sum),
(5, 'avg1', avg1),
(6, 'avg2', avg2),
(7, 'a1', a1),
(8, 'a2', a2),
(9, 'a3', a3)
) c (sort_order, col, value)
See SQL Fiddle with Demo. Once the data has been unpivoted, then you can pivot using the new columns which are the id values. So the full code is:
select col,
id1,
id40,
id41,
id42
from
(
select 'id'+cast(t.id as varchar(10)) p_id,
c.col,
c.value,
c.sort_order
from yourtable t
cross apply
(
values
(1, 'id', id),
(2, 'size', size),
(3, 'acted', acted),
(4, 'sum', sum),
(5, 'avg1', avg1),
(6, 'avg2', avg2),
(7, 'a1', a1),
(8, 'a2', a2),
(9, 'a3', a3)
) c (sort_order, col, value)
) src
pivot
(
max(value)
for p_id in (id1, id40, id41, id42)
) piv
order by sort_order;
See SQL Fiddle with Demo.
If you cannot use the CROSS APPLY and VALUES, then this can also be done, using the UNPIVOT function:
select col,
id1, id40, id41, id42
from
(
select 'id'+cast(id_piv as varchar(10)) id,
col,
value,
case col
when 'id' then 1
when 'size' then 2
when 'acted' then 3
when 'sum' then 4
when 'avg1' then 5
when 'avg2' then 6
when 'a1' then 7
when 'a2' then 8
when 'a3' then 9 end sort_order
from
(
select id id_piv,
cast(id as numeric(10, 2)) id,
cast(size as numeric(10, 2)) size,
cast(acted as numeric(10, 2)) acted,
sum, avg1, avg2, A1, A2, A3
from yourtable
) d
unpivot
(
value
for col in (id, size, acted, sum, avg1, avg2, a1, a2, a3)
) unpiv
) src
pivot
(
max(value)
for id in (id1, id40, id41, id42)
) piv
order by sort_order;
See SQL Fiddle with Demo
Finally, if you are going to have an unknown number of id values that you want to convert to columns, then you will need to use dynamic sql:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME('id'+cast(id as varchar(10)))
from yourtable
group by id
order by id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT col, ' + #cols + '
from
(
select ''id''+cast(t.id as varchar(10)) p_id,
c.col,
c.value,
c.sort_order
from yourtable t
cross apply
(
values
(1, ''id'', id),
(2, ''size'', size),
(3, ''acted'', acted),
(4, ''sum'', sum),
(5, ''avg1'', avg1),
(6, ''avg2'', avg2),
(7, ''a1'', a1),
(8, ''a2'', a2),
(9, ''a3'', a3)
) c (sort_order, col, value)
) x
pivot
(
max(value)
for p_id in (' + #cols + ')
) p
order by sort_order'
execute(#query)
See SQL Fiddle with Demo
All versions the result:
| COL | ID1 | ID40 | ID41 | ID42 |
----------------------------------------------
| id | 1 | 40 | 41 | 42 |
| size | 3921 | 11979 | 11714 | 11599 |
| acted | 39 | 301 | 289 | 265 |
| sum | 690 | 5944.26 | 5060 | 4107.98 |
| avg1 | 17.69 | 19.75 | 17.51 | 15.5 |
| avg2 | 0.18 | 0.5 | 0.43 | 0.35 |
| a1 | (null) | 10000 | 10000 | 10000 |
| a2 | (null) | 2000 | 3000 | 5000 |
| a3 | (null) | 1000 | 2000 | 500 |

Related

Join on existing aggregate query to pivot result without id

On a Sql-Server instance, I have three tables:
ActionItem
Id
Name
1
Fish
2
Gravy
3
Pants
ActionData
Id
ActionId
Group
Field
Value
1
1
1
1
100
2
1
1
2
200
3
1
1
3
300
4
1
1
4
NULL
5
1
1
5
NULL
6
1
2
6
"Some Text"
7
2
1
1
50
8
2
1
2
60
9
2
1
3
70
Costing
Id
ActionId
Break
Cost
1
1
Normal
11.3
2
1
Sub
54
3
1
Premium
0.4
4
3
Normal
22
5
3
Premium
0.67
I have a query that sums the cost for each ActionItem:
select
ai.Id,
ai.Name,
sum(c.Cost)
from ActionItem ai
left join Costing c on ai.Id = c.ActionId
group by
ai.Id,
ai.Name
Nice and straight-forward:
Id
Name
(No column name)
1
Fish
65.7
2
Gravy
NULL
3
Pants
22.67
I created a pivot too:
select * from
(select [ActionId], [Group], [Field], [Value] from ActionData) src
pivot (max([Value]) for [ActionId] in ([1],[2],[3],[4])) ppp
Which gets me data in the right format:
Group
Field
1
2
3
4
1
1
100
50
NULL
NULL
1
2
200
60
NULL
NULL
1
3
300
70
NULL
NULL
1
4
NULL
NULL
NULL
NULL
1
5
NULL
NULL
NULL
NULL
2
6
"Some Text"
NULL
NULL
NULL
But I cannot join these two queries together because that PIVOT doesn't contain the ActionId ... even though I use Select * from - how can I get the ActionId col to show on my pivoted data, so I can join it to the rest of my original query?
I could not get sqlfiddle.com to work for MS SQL SERVER today but here are create and inserts if anyone's interested:
CREATE TABLE ActionItem
([Id] int, [Name] varchar(5));
INSERT INTO ActionItem
([Id], [Name])
VALUES
(1, 'Fish'),
(2, 'Gravy'),
(3, 'Pants');
CREATE TABLE ActionData
([Id] int, [ActionId] int, [Group] int, [Field] int, [Value] varchar(11));
INSERT INTO ActionData
([Id], [ActionId], [Group], [Field], [Value])
VALUES
(1, 1, 1, 1, '100'),
(2, 1, 1, 2, '200'),
(3, 1, 1, 3, '300'),
(4, 1, 1, 4, NULL),
(5, 1, 1, 5, NULL),
(6, 1, 2, 6, '"Some Text"'),
(7, 2, 1, 1, '50'),
(8, 2, 1, 2, '60'),
(9, 2, 1, 3, '70')
;
CREATE TABLE Costing (
[Id] int,
[ActionId] int,
[Break] VARCHAR(9),
[Cost] FLOAT);
INSERT INTO Costing
([Id], [ActionId], [Break], [Cost])
VALUES
('1', '1', 'Normal', '11.3'),
('2', '1', 'Sub', '54'),
('3', '1', 'Premium', '0.4'),
('4', '3', 'Normal', '22'),
('5', '3', 'Premium', '0.67');
Not sure what output you expect.
But here's an attempt to join the two queries in 1 pivot.
select pvt.*
from
(
select d.ActionId, ai.Name
--, d.[Group]
, cast(d.[Field] as varchar(30)) as [Col]
, try_cast(d.[Value] as float) as [Value]
from ActionData d
left join ActionItem ai on ai.Id = d.ActionId
where isnumeric(d.[Value]) = 1
union all
select c.ActionId, ai.Name
--, 1 as [Group]
, c.[Break] as [Col]
, sum(c.Cost) as TotalCost
from Costing c
left join ActionItem ai
on ai.Id = c.ActionId
group by c.ActionId, ai.Name, c.[Break]
) src
pivot (
max([Value])
for [Col] in ([1],[2],[3],[4],[Normal],[Premium],[Sub])
) pvt
GO
ActionId | Name | 1 | 2 | 3 | 4 | Normal | Premium | Sub
-------: | :---- | ---: | ---: | ---: | ---: | -----: | ------: | ---:
1 | Fish | 100 | 200 | 300 | null | 11.3 | 0.4 | 54
2 | Gravy | 50 | 60 | 70 | null | null | null | null
3 | Pants | null | null | null | null | 22 | 0.67 | null
db<>fiddle here

How to build up a sparse output from given records in SQL Server statements?

Given some records
declare #t table
(
idx varchar(10),
class varchar(10),
head varchar(10),
qty VARCHAR(10)
)
insert #t (idx, class, head, qty)
values ('row1', 'H1', 'C1', 1), ('row1', 'H1', 'C2', 2),
('row1', 'H1', 'C3', 3), ('row2', 'H2', 'D1', 2),
('row2', 'H2', 'D2', 3), ('row2', 'H2', 'D3', 4),
('row3', 'H1', 'C2', 8), ('row3', 'H2', 'D2', 9),
('row3', 'H2', 'D3', 10), ('row4', '', '', ''),
('row5', 'H2', 'D2', 10), ('row5', 'H2', 'D3', 11),
('row5', 'H3', 'E1', 12), ('row6', '','','')
SELECT * FROM #t
OUTPUT:
idx memo class head qty
--------------------------------
row1 ida H1 C1 1
row1 ida H1 C2 2
row1 ida H1 C3 3
row2 id H2 D1 2
row2 id H2 D2 3
row2 id H2 D3 4
row3 id H1 C2 8
row3 id H2 D2 9
row3 id H2 D3 10
row4 ida
row5 idf H2 D2 10
row5 idf H2 D3 11
row5 idf H3 E1 12
row6 id
How to make a spare array output like below efficiently? ZEROs could be replaced by blank string. A similar question and answer in Mathematica could be found here.https://mathematica.stackexchange.com/questions/186835/building-a-sparse-array-from-given-lists-the-2nd-case
"arrays" are not something SQL is generally known for although Postgres does have array features.
In T-SQL you can "pivot" you table, like this:
SELECT idx, [C1],[C2],[C3],[D1],[D2],[D3],[E1]
FROM (
SELECT
idx, head, qty
FROM #t
) sourcedata
pivot
(
max([qty])
FOR [head] IN ([C1],[C2],[C3],[D1],[D2],[D3],[E1])
) p
order by idx
which will produce this:
+----+------+------+------+------+------+------+------+------+
| | idx | C1 | C2 | C3 | D1 | D2 | D3 | E1 |
+----+------+------+------+------+------+------+------+------+
| 1 | row1 | 1 | 2 | 3 | NULL | NULL | NULL | NULL |
| 2 | row2 | NULL | NULL | NULL | 2 | 3 | 4 | NULL |
| 3 | row3 | NULL | 8 | NULL | NULL | 9 | 10 | NULL |
| 4 | row4 | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
| 5 | row5 | NULL | NULL | NULL | NULL | 10 | 11 | 12 |
| 6 | row6 | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
+----+------+------+------+------+------+------+------+------+
and you can even generate the pivot query if needed:
DECLARE #cols AS NVARCHAR(MAX)
DECLARE #query AS NVARCHAR(MAX)
SET #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(head)
FROM some_table s
where head is not null and head <> ''
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = 'SELECT idx, ' + #cols + '
FROM (
SELECT
idx, head, qty
FROM some_table
) sourcedata
pivot
(
max([qty])
FOR [idx] IN (' + #cols + ')
) p
order by idx '
select #query -- use select to inspect the generated sql
--execute(#query) -- once satisfied that sql is OK, use execute
but you can't use #t as the data source when executing the #query.
I have no idea if this really helps because as there no columnar references back to Classes
EDIT
To replace NULLs in the final output requires changing the first query seen above to this:
SELECT
idx
, COALESCE( CAST( [C1] AS varchar ), '' ) -- converted into STRINGS
, COALESCE( CAST( [C2] AS varchar ), '' )
, COALESCE( CAST( [C3] AS varchar ), '' )
, COALESCE( CAST( [D1] AS varchar ), '' )
, COALESCE( CAST( [D2] AS varchar ), '' )
, COALESCE( CAST( [D3] AS varchar ), '' )
, COALESCE( CAST( [E1] AS varchar ), '' )
FROM (
SELECT
idx
, head
, qty
FROM #t
) sourcedata
PIVOT
(
MAX( [qty] )
FOR [head] IN ([C1], [C2], [C3], [D1], [D2], [D3], [E1])
) p
ORDER BY
idx

Grouping by and getting multiple rows to one cell separated by commas

I know that title is ambigious and I know that there are similiar questions here. I looked and could not made anything work exactly as I need.
I need to Group By ReportNumber and those sharing one category add to one cell.
My DB:
ID | CategorySymbol | ReportNumber | NumberInCategory
1 A 31 101
2 B 31 107
3 C 31 121
4 A 32 191
5 A 33 165
6 B 32 156
7 C 32 127
8 A 31 166
9 B 31 177
Desired result:
ReporNumber | CategoryA | CategoryB | CategoryC
31 **101,166** **107,177** 121
32 191 156 127
33 165 NULL NULL
One of many different attempts:
select ReportNumber,
CategoryFirst, CategorySecond, CategoryThird, CategoryFourth
from
(
select NumberInCategory, Report.value('(/Report/#ReportNumber)[1]', 'nvarchar(max)') AS ReportNumber,
'Category' + cast(CategorySymbol as varchar(10)) CategorySymbol
from dbo.ReportDB t
) d
pivot
(
max(NumberInCategory)
for CategorySymbol in (CategoryFirst, CategorySecond, CategoryThird, CategoryFourth)
) piv;
My result:
ReporNumber | CategoryA | CategoryB | CategoryC
31 **166** **177** 121
32 191 156 127
33 165 NULL NULL
It is obvious why the result is how it is - max(NumberInCategory). The only question is how do I get to make a query that selects Number based on the CategorySymbol in for loop. I tried doing funtions that return one result like STUFF or simple SELECT but couldn't do them properly. It does not let me replace max(NumberInCategory).
E.g. something like that:
STUFF((SELECT ', ' + CAST(NumberInCategory AS VARCHAR(10)) [text()], Report.value('(/Report/#ReportNumber)[1]', 'nvarchar(max)') AS ReportNumber
FROM ReportDB
WHERE ReportNumber = t.ReportNumber
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') List_Output
Using the stuff() with select ... for xml path ('') method of string concatenation before pivot():
;with t as (
select
NumberInCategory
, Report.value('(/Report/#ReportNumber)[1]', 'nvarchar(max)') AS ReportNumber
, CategorySymbol
from dbo.ReportDB
)
select ReportNumber, CategoryA, CategoryB, CategoryC
from (
select
t.ReportNumber
, CategorySymbol = 'Category'+convert(varchar(10),t.CategorySymbol)
, NumberInCategory = stuff((
select ', '+convert(varchar(13),i.NumberInCategory)
from t i
where i.ReportNumber = t.ReportNumber
and i.CategorySymbol = t.CategorySymbol
order by i.NumberInCategory
for xml path (''), type).value('(./text())[1]','nvarchar(max)')
,1,2,'')
from t
group by t.ReportNumber, t.CategorySymbol
) s
pivot (max(NumberInCategory)
for CategorySymbol in (CategoryA, CategoryB, CategoryC)
) piv;
rextester demo: http://rextester.com/OSAZ69656
returns:
+--------------+-----------+-----------+-----------+
| ReportNumber | CategoryA | CategoryB | CategoryC |
+--------------+-----------+-----------+-----------+
| 31 | 101, 166 | 107, 177 | 121 |
| 32 | 191 | 156 | 127 |
| 33 | 165 | NULL | NULL |
+--------------+-----------+-----------+-----------+
Here's a slightly different flavor...
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #TestData;
CREATE TABLE #TestData (
ID INT NOT NULL PRIMARY KEY CLUSTERED,
CategorySymbol CHAR(1) NOT NULL,
ReportNumber int NOT NULL,
NumberInCategory INT NOT NULL
);
INSERT #TestData(ID, CategorySymbol, ReportNumber, NumberInCategory) VALUES
(1, 'A', 31, 101),
(2, 'B', 31, 107),
(3, 'C', 31, 121),
(4, 'A', 32, 191),
(5, 'A', 33, 165),
(6, 'B', 32, 156),
(7, 'C', 32, 127),
(8, 'A', 31, 166),
(9, 'B', 31, 177);
-- SELECT * FROM #TestData td;
--==================================================================================
SELECT
td1.ReportNumber,
CategoryA = MAX(CASE WHEN td1.CategorySymbol = 'A' THEN STUFF(c.CSV, 1, 1, '') END),
CategoryB = MAX(CASE WHEN td1.CategorySymbol = 'B' THEN STUFF(c.CSV, 1, 1, '') END),
CategoryC = MAX(CASE WHEN td1.CategorySymbol = 'C' THEN STUFF(c.CSV, 1, 1, '') END)
FROM
#TestData td1
CROSS APPLY (
(SELECT
CONCAT(',', td2.NumberInCategory)
FROM
#TestData td2
WHERE
td1.CategorySymbol = td2.CategorySymbol
AND td1.ReportNumber = td2.ReportNumber
FOR XML PATH(''))
) c (CSV)
GROUP BY
td1.ReportNumber;
Results...
ReportNumber CategoryA CategoryB CategoryC
------------ ---------- ---------- ----------
31 101,166 107,177 121
32 191 156 127
33 165 NULL NULL

Pivot Table Missing Column

I am trying to use a pivot to get information in a diff format.
Here is my table:
CREATE TABLE yourtable
([case] int, [category] varchar(4))
;
INSERT INTO yourtable
([case], [category])
VALUES
(1, 'xx'),
(1, 'xyx'),
(1, 'abc'),
(2, 'ghj'),
(2, 'asdf'),
(3, 'dfgh')
;
Here is my pivot command courtesy of bluefeet:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME('cat'+cast(seq as
varchar(10)))
from
(
select row_number() over(partition by [case]
order by category) seq
from yourtable
) d
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT [case],' + #cols + '
from
(
SELECT [case], category,
''cat''+
cast(row_number() over(partition by [case]
order by category) as varchar(10)) seq
FROM yourTable
) x
pivot
(
max(category)
for seq in (' + #cols + ')
) p '
execute sp_executesql #query;
The output is good, it is in the format I need.
CASE CAT1 CAT2 CAT3
1 abc xx xyx
2 asdf ghj (null)
3 dfgh (null) (null)
However, I also need to add additional columns to the table. The modified table would be as follows, but I'm not sure how to add this to the QUOTENAME.
CREATE TABLE yourtable
([case] int, [category] varchar(4), [status] varchar(4))
;
INSERT INTO yourtable
([case], [category], [status])
VALUES
(1, 'xx', '00'),
(1, 'xyx', '01'),
(1, 'abc', '00'),
(2, 'ghj', '01'),
(2, 'asdf', '00'),
(3, 'dfgh', '01')
;
How can this be done? Should I add an additional QUOTENAME command? Results should be:
CASE CAT1 status1 CAT2 status2 CAT3 status3
1 abc 00 xx 00 xyx 01
2 asdf 00 ghj 01 (null) (null)
3 dfgh 01 (null) (null) (null) (null)
Since you now have two columns that you want to PIVOT, you can first unpivot the category and status columns into a single column with multiple rows.
There are a few different ways you can unpivot the data, you can use UNPIVOT or CROSS APPLY. The basic syntax will be:
select [case],
col+cast(seq as varchar(10)) seq,
value
from
(
SELECT [case], status, category,
row_number() over(partition by [case]
order by status) seq
FROM yourTable
) d
cross apply
(
select 'cat', category union all
select 'status', status
) c (col, value)
See SQL Fiddle with Demo This will convert your multiple columns of data into something that looks like this:
| CASE | SEQ | VALUE |
|------|---------|-------|
| 1 | cat1 | xx |
| 1 | status1 | 00 |
| 1 | cat2 | abc |
| 1 | status2 | 00 |
| 1 | cat3 | xyx |
| 1 | status3 | 01 |
| 2 | cat1 | asdf |
| 2 | status1 | 00 |
Once the data is in this format, then you can apply the PIVOT function to it.
SELECT [case], cat1, status1, cat2, status2, cat3, status3
FROM
(
select [case],
col+cast(seq as varchar(10)) seq,
value
from
(
SELECT [case], status, category,
row_number() over(partition by [case]
order by status) seq
FROM yourTable
) d
cross apply
(
select 'cat', category union all
select 'status', status
) c (col, value)
) x
PIVOT
(
max(value)
for seq in (cat1, status1, cat2, status2, cat3, status3)
)p;
See SQL Fiddle with Demo
Then you can convert it to dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col+cast(seq as varchar(10)))
from
(
select row_number() over(partition by [case]
order by category) seq
from yourtable
) d
cross apply
(
select 'cat', 1 union all
select 'status', 2
) c (col, so)
group by seq, col, so
order by seq, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT [case],' + #cols + '
from
(
select [case],
col+cast(seq as varchar(10)) seq,
value
from
(
SELECT [case], status, category,
row_number() over(partition by [case]
order by status) seq
FROM yourTable
) d
cross apply
(
select ''cat'', category union all
select ''status'', status
) c (col, value)
) x
pivot
(
max(value)
for seq in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo The final result will be:
| CASE | CAT1 | STATUS1 | CAT2 | STATUS2 | CAT3 | STATUS3 |
|------|------|---------|--------|---------|--------|---------|
| 1 | xx | 00 | abc | 00 | xyx | 01 |
| 2 | asdf | 00 | ghj | 01 | (null) | (null) |
| 3 | dfgh | 01 | (null) | (null) | (null) | (null) |

Is it possible to Pivot data like this?

I have data that looks as follows:
I would like to pivot this data so that there is only a single row for the SubId. The columns would be SubId, 802Lineage, 802ReadTime, 1000Lineage, 1000ReadTime etc.
If it wasn't for the requirement to have the Lineage included, this would be pretty straightforward, as follows:
Select SubId, [800] as [800Time], [1190] as [1190Time], [1605] as [1605Time]
From
(Select SubId, ProcessNumber, ReadTime From MyTable) as SourceTable
PIVOT
(
Max(ReadTime)
For ProcessNumber IN ([800],[802],[1190],[1605])
) as PivotTable;
I'm just not sure how to do this with the Lineage included. This is for SQL Server 2012
you can pivot manually:
select
SubId,
max(case when ProcessNumber = 802 then ReadTime end) as [802Time],
max(case when ProcessNumber = 802 then Lineage end) as [802Lineage],
....
from SourceTable
group by SubId
Example of joining two pivot tables, as requested in comments.
CREATE TABLE #MyTable (SubId int, ProcessNumber int, Lineage varchar(16), ReadTime datetime)
INSERT INTO #MyTable 
(SubID, ProcessNumber, Lineage, ReadTime)
VALUES
(1, 9, 'A', GETDATE()),
(1, 8, 'A', GETDATE()),
(1, 7, 'B', GETDATE()),
(2, 9, 'C', GETDATE()),
(2, 8, 'C', GETDATE())
SELECT * 
FROM (
Select SubId, [9] as [9Time], [8] as [8Time], [7] as [7Time]
From
(Select SubId, ProcessNumber, ReadTime From #MyTable) as SourceTable
PIVOT(Max(ReadTime) For ProcessNumber IN ([9],[8],[7],[6])) as PivotTable1
) AS T1
INNER JOIN (
Select SubId, [9] as [9Lineage], [8] as [8Lineage], [7] as [7Lineage]
From
(Select SubId, ProcessNumber, Lineage From #MyTable) as SourceTable
PIVOT(Max(Lineage) For ProcessNumber IN ([9],[8],[7],[6])) as PivotTable1
) AS T2
ON T1.SubId = T2.SubId
GO
You can use the PIVOT function to get the result but you will have to unpivot the Lineage and ReadTime columns from the multiple columns into multiple rows.
Since you are using SQL Server 2012 you can unpivot the data using CROSS APPLY with VALUES:
select subid,
colname = cast(processNumber as varchar(10)) + colname,
value
from mytable
cross apply
(
values
('Lineage', Lineage),
('ReadTime', convert(varchar(20), readtime, 120))
) c (colname, value)
See SQL Fiddle with Demo. This will convert your current data into the format:
| SUBID | COLNAME | VALUE |
|-------------|--------------|---------------------|
| 12010231146 | 802Lineage | PBG12A |
| 12010231146 | 802ReadTime | 2012-01-02 21:44:00 |
| 12010231146 | 1000Lineage | PBG12A |
| 12010231146 | 1000ReadTime | 2012-01-02 21:43:00 |
| 12010231146 | 1190Lineage | PBG11B |
| 12010231146 | 1190ReadTime | 2012-01-03 14:36:00 |
Once the data is in this format, then you can easily apply the PIVOT function to get your final result:
select *
from
(
select subid,
colname = cast(processNumber as varchar(10)) + colname,
value
from mytable
cross apply
(
values
('Lineage', Lineage),
('ReadTime', convert(varchar(20), readtime, 120))
) c (colname, value)
) d
pivot
(
max(value)
for colname in ([802Lineage], [802ReadTime],
[1000Lineage], [1000ReadTime],
[1190Lineage], [1190ReadTime])
) piv;
See SQL Fiddle with Demo.
The above works great if you have a limited number of rows that you want to convert, but if you have an unknown number then you can use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(cast(processnumber as varchar(10))+col)
from mytable
cross apply
(
select 'Lineage', 0 union all
select 'ReadTime', 1
) c (col, so)
group by processnumber, col, so
order by processnumber, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT subid, ' + #cols + '
from
(
select subid,
colname = cast(processNumber as varchar(10)) + colname,
value
from mytable
cross apply
(
values
(''Lineage'', Lineage),
(''ReadTime'', convert(varchar(20), readtime, 120))
) c (colname, value)
) x
pivot
(
max(value)
for colname in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo. This gives a result:
| SUBID | 802LINEAGE | 802READTIME | 1000LINEAGE | 1000READTIME | 1190LINEAGE | 1190READTIME | 1605LINEAGE | 1605READTIME | 1745LINEAGE | 1745READTIME | 1790LINEAGE | 1790READTIME | 1990LINEAGE | 1990READTIME | 2690LINEAGE | 2690READTIME | 2795LINEAGE | 2795READTIME | 2990LINEAGE | 2990READTIME | 3090LINEAGE | 3090READTIME | 3290LINEAGE | 3290READTIME |
|-------------|------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|-------------|---------------------|
| 12010231146 | PBG12A | 2012-01-02 21:44:00 | PBG12A | 2012-01-02 21:43:00 | PBG11B | 2012-01-03 14:36:00 | PBG11B | 2012-01-03 15:15:00 | PBG11A | 2012-01-03 15:16:00 | PBG11A | 2012-01-03 15:19:00 | PBG11A | 2012-01-03 15:23:00 | PBG11A | 2012-01-03 15:32:00 | PBG11A | 2012-01-03 15:39:00 | PBG11A | 2012-01-03 15:41:00 | PBG11A | 2012-01-03 15:46:00 | PBG11A | 2012-01-03 15:47:00 |