Is it possible to Pivot data like this? - sql

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 |

Related

SQL query in out time attendance each days

I'm a newbie in SQL Server; I have trouble to write this query.
This is my sample data:
| Id | date | time |
+----+-----------+------------+
| 1 | 2020-11-1 | 07:30:00 |
| 1 | 2020-11-1 | 15:50:00 |
| 2 | 2020-11-1 | 07:30:00 |
| 2 | 2020-11-1 | 15:54:00 |
| 1 | 2020-11-2 | 07:31:00 |
| 1 | 2020-11-2 | 15:46:00 |
This is the result I need:
| id |in_2020_11_01 | out_2020_11_01 | in_2020_11_02 | out_2020_11_02 |
+----+--------------+-----------------+----------------+----------------+
| 1 | 07:30:00 | 15:50:00 | 07:31:00 | 15:46:00 |
| 2 | 07:30:00 | 15:54:00 | 00:00:00 | 00:00:00 |
How can I build the result? Is it possible to get a result like that?
Thanks in advance
You need to use dynamic T-SQL and build a PIVOT statement. Here is full working example:
DROP TABLE IF EXISTS #DataSource;
CREATE TABLE #DataSource
(
[Id] INT
,[date] DATE
,[time] TIME
);
INSERT INTO #DataSource ([Id], [date], [time])
VALUES (1, '2020-11-1', '07:30:00')
,(1, '2020-11-1', '15:50:00')
,(2, '2020-11-1', '07:30:00')
,(2, '2020-11-1', '15:54:00')
,(1, '2020-11-2', '07:31:00')
,(1, '2020-11-2', '15:46:00');
DECLARE #ColumnsPIVOT VARCHAR(MAX)
,#ColumnsSELECT VARCHAR(MAX)
,#DyanmicTSQLSTatement NVARCHAR(MAX);
WITH DataSource ([date], [prefix], [column_name]) AS
(
SELECT DISTINCT [date]
,[prefix]
,[prefix] + '_' + REPLACE([date], '-', '_')
FROM #DataSource
CROSS APPLY
(
VALUES ('in')
,('out')
) DS ([prefix])
)
SELECT #ColumnsPIVOT = STUFF
(
(
SELECT ',' + QUOTENAME([column_name])
FROM DataSource
ORDER BY [date]
,[prefix]
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
,1
,1
,''
)
,#ColumnsSELECT = STUFF
(
(
SELECT ', ISNULL(' + QUOTENAME([column_name]) + ', ''00:00:00'') AS ' + QUOTENAME([column_name])
FROM DataSource
ORDER BY [date]
,[prefix]
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
,1
,1
,''
);
-- SQL Server 2017+
/*
WITH DataSource ([date], [prefix], [column_name]) AS
(
SELECT DISTINCT [date]
,[prefix]
,[prefix] + '_' + REPLACE([date], '-', '_')
FROM #DataSource
CROSS APPLY
(
VALUES ('in')
,('out')
) DS ([prefix])
)
SELECT #ColumnsPIVOT = STRING_AGG(QUOTENAME([column_name]), ',') WITHIN GROUP(ORDER BY [date], [prefix])
,#ColumnsSELECT = STRING_AGG('ISNULL(' + QUOTENAME([column_name]) + ', ''00:00:00'') AS ' + QUOTENAME([column_name]), ',') WITHIN GROUP(ORDER BY [date], [prefix])
FROM DataSource;
*/
SET #DyanmicTSQLSTatement= 'SELECT [ID], ' + #ColumnsSELECT + '
FROM
(
SELECT [id]
,CASE ROW_NUMBER() OVER (PARTITION BY [ID], [Date] ORDER BY [Time])
WHEN 1 THEN ''in''
WHEN 2 THEN ''out''
END + ''_'' + REPLACE([date], ''-'', ''_'')
,[time]
FROM #DataSource
) DS ([id], [date], [time])
PIVOT
(
MAX([time]) FOR [date] IN (' + #ColumnsPIVOT +')
) PVT';
EXEC sp_executesql #DyanmicTSQLSTatement;

Convert row into column like pivot

Table is:
+----+------+
| Id | Name |
+----+------+
| 1 | aaa |
| 1 | bbb |
| 2 | ccc |
| 2 | ddd |
| 3 | eee |
+----+------+
Required output:
+----+---------------------++---------------------+
| Id | colum1 | column2 |
+----+---------------------+ +--------------------+
| 1 | aaa | | bbb |
+----+---------------------++---------------------+
+----+---------------------+ +--------------------+
| 2 | ccc | | ddd |
+----+---------------------++---------------------+
+----+---------------------+ +--------------------+
| 3 | eee | | null |
+----+---------------------++---------------------+
I've been trying on 'with' a and pivot but it seems not in the right way I want a column if I have more than one id
like the image
You can use row_number() & do aggregation if you have a some limited amount of names else you would need to use dynamic SQL for this:
select id,
max(case when seq = 1 then name end) as col1,
max(case when seq = 2 then name end) as col2,
max(case when seq = 3 then name end) as col3,
. . .
from (select t.*, row_number() over (partition by id order by name) as seq
from table t
) t
group by id;
You can use PIVOT for making this.
DECLARE #Tbl TABLE ( Id INT, Name VARCHAR(10))
INSERT INTO #Tbl VALUES
(1, 'aaa'),
(1, 'bbb'),
(2, 'ccc'),
(2, 'ddd'),
(3, 'eee')
SELECT Id, [1] column1, [2] column2 FROM (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY Id ORDER BY Name) RN
FROM #Tbl ) AS SRC PIVOT (MAX(Name) FOR RN IN ([1], [2])) PVT
Result:
Id column1 column2
----------- ---------- ----------
1 aaa bbb
2 ccc ddd
3 eee NULL
Using row_number() build a dynamic query to execute()
--example table
create table #t (Id int, Name varchar(100))
insert into #t values (1,'aaa'),(1,'bbb'),(2,'ccc'),(2,'ddd'),(3,'eee')
-- number of columns to create
declare #columns int
select top 1 #columns = count(*) from #t group by id order by COUNT(*) desc
--build a query
declare #i int = 2, #qry varchar(max) = 'select Id, column1 = max(case when ord = 1 then name end)'
while #i<=#columns begin
select #qry = #qry + ', column'+cast(#i as varchar(5))+' = max(case when ord = '+cast(#i as varchar(5))+' then name end)'
set #i = #i + 1
end
select #qry = #qry + ' from (select *, ord = row_number() over (partition by id order by name)
from #t
) t
group by id'
--execute the query
execute (#qry)
drop table #t

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) |

sql server group columns as rows (pivot?)

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 |

Add more columns for each value of another column?

I have this kind of table :
Name Date Value
-----------------------
Test 1/1/2001 10
Test 2/1/2001 17
Test 3/1/2001 52
Foo 5/4/2011 15
Foo 6/4/2011 321
My 15/5/2005 36
My 25/7/2005 75
And I would like to show the results like this :
Name Date Value Name Date Value Name Date Value
---------------------------------------------------------------------
Test 1/1/2001 10 Foo 5/4/2011 15 My 15/5/2005 36
Test 2/1/2001 17 Foo 6/4/2011 321 My 25/7/2005 75
Test 3/1/2001 52
I need to show as many columns as what is present in my Name column
How could I do this in Sql ?
In order to get the result that you want, you are going to have to unpivot the columns in your table and apply the pivot function.
The unpivot can be done using either the UNPIVOT function or you can use CROSS APPLY with VALUES.
UNPIVOT:
select rn,
col +'_'+cast(dr as varchar(10)) col,
new_values
from
(
select name,
convert(varchar(10), date, 101) date,
cast(value as varchar(10)) value,
dense_rank() over(order by name) dr,
row_number() over(partition by name order by date) rn
from yourtable
) d
unpivot
(
new_values
for col in (name, date, value)
) un;
CROSS APPLY:
select rn,
col +'_'+cast(dr as varchar(10)) col,
c.value
from
(
select name,
convert(varchar(10), date, 101) date,
cast(value as varchar(10)) value,
dense_rank() over(order by name) dr,
row_number() over(partition by name order by date) rn
from yourtable
) d
cross apply
(
values
('Name', name), ('Date', date), ('Value', Value)
) c (col, value);
See SQL Fiddle with Demo of both versions. This gives the result:
| RN | COL | NEW_VALUES |
-----------------------------
| 1 | name_1 | Foo |
| 1 | date_1 | 04/05/2011 |
| 1 | value_1 | 15 |
| 2 | name_1 | Foo |
| 2 | date_1 | 04/06/2011 |
| 2 | value_1 | 321 |
| 1 | name_2 | My |
| 1 | date_2 | 05/15/2005 |
| 1 | value_2 | 36 |
These queries take your existing columns values and converts them to rows. Once they are in rows, you create the new column names by using the windowing function dense_rank.
Once the data has been converted to rows, you then use the new column names (created with the dense_rank value) and apply the PIVOT function.
PIVOT with UNPIVOT:
select name_1, date_1, value_1,
name_2, date_2, value_2,
name_3, date_3, value_3
from
(
select rn,
col +'_'+cast(dr as varchar(10)) col,
new_values
from
(
select name,
convert(varchar(10), date, 101) date,
cast(value as varchar(10)) value,
dense_rank() over(order by name) dr,
row_number() over(partition by name order by date) rn
from yourtable
) d
unpivot
(
new_values
for col in (name, date, value)
) un
) src
pivot
(
max(new_values)
for col in (name_1, date_1, value_1,
name_2, date_2, value_2,
name_3, date_3, value_3)
) piv;
See SQL Fiddle with Demo
PIVOT with CROSS APPLY:
select name_1, date_1, value_1,
name_2, date_2, value_2,
name_3, date_3, value_3
from
(
select rn,
col +'_'+cast(dr as varchar(10)) col,
c.value
from
(
select name,
convert(varchar(10), date, 101) date,
cast(value as varchar(10)) value,
dense_rank() over(order by name) dr,
row_number() over(partition by name order by date) rn
from yourtable
) d
cross apply
(
values
('Name', name), ('Date', date), ('Value', Value)
) c (col, value)
) src
pivot
(
max(value)
for col in (name_1, date_1, value_1,
name_2, date_2, value_2,
name_3, date_3, value_3)
) piv;
See SQL Fiddle with Demo.
Dyanmic PIVOT:
The above versions will work great if you have a limited or known number of columns, if not, then you will need to use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col +'_'+cast(dr as varchar(10)))
from
(
select dense_rank() over(order by name) dr
from yourtable
) t
cross apply
(
values(1, 'Name'), (2, 'Date'), (3, 'Value')
) c (sort, col)
group by col, dr, sort
order by dr, sort
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ' + #cols + '
from
(
select rn,
col +''_''+cast(dr as varchar(10)) col,
c.value
from
(
select name,
convert(varchar(10), date, 101) date,
cast(value as varchar(10)) value,
dense_rank() over(order by name) dr,
row_number() over(partition by name order by date) rn
from yourtable
) d
cross apply
(
values
(''Name'', name), (''Date'', date), (''Value'', Value)
) c (col, value)
) x
pivot
(
max(value)
for col in (' + #cols + ')
) p'
execute(#query)
See SQL Fiddle with Demo.
The result for each of the queries is:
| NAME_1 | DATE_1 | VALUE_1 | NAME_2 | DATE_2 | VALUE_2 | NAME_3 | DATE_3 | VALUE_3 |
-------------------------------------------------------------------------------------------------
| Foo | 04/05/2011 | 15 | My | 05/15/2005 | 36 | Test | 01/01/2001 | 10 |
| Foo | 04/06/2011 | 321 | My | 07/25/2005 | 75 | Test | 01/02/2001 | 17 |
| (null) | (null) | (null) | (null) | (null) | (null) | Test | 01/03/2001 | 52 |
By hand or with a program. Most people are accustomed to showing output in a linear fashion (the default). If someone is asking you to do this, you can tell them that's not how the application works. You can export the result set to csv and then import that into something like Excel and reformat it by hand or use a serverside language like ASP.net or PHP to format the results into a table.
When you're parsing the output you could check the last var Name against the current. If they're different then add a column. It would still be tricky to script it because they will more than likely come out of the database in order. So you would have a sequence like test, test, test, foo, foo which would mean that you need to create a multidimensional array to organize the data to get a column count. Then setup the table based on that with a counter that counts row names, then data underneath.
I'm not sure which apps you're familiar with, so in PHP the output would look something like this from the multidimensional array.
row [1]['name']=test
row [1][test][1]['date'] = 1/1/2001
This is more of a visual output though. The databases are designed to hold data and return it in an intuitive fashion.