Combining multiple rows in TSQL by case number - sql

I'm new to SQL (more precisely T-SQL) and I can't seem to wrap my head around this one. I'm sure there is a simple solution and I'm just not thinking of (maybe involving subqueries and/or a table pivot). But I was hoping one of you SQL whizzes could help out a clueless newb.
Basically, I need to turn this data:
CaseNumber|DecisionNumber|Date |Decision
----------+--------------+-----------+--------
444 |29833 |04/05/2005 |Sell
444 |29777 |05/10/2006 |Sell
444 |29654 |08/19/2007 |Buy
468 |29230 |08/19/2006 |Sell
468 |29192 |08/19/2011 |Sell
Into this result:
CaseNumber|DecisionNumber1|Date1 |Decision1|DecisionNumber2|Date2 |Decision2|DecisionNumber3|Date3 |Decision3
----------+---------------+------------+---------+---------------+------------+---------+---------------+------------+---------
444 |29833 |04/05/2005 |Sell |29777 |05/10/2006 |Sell |29654 |08/19/2007 |Buy
468 |29230 |08/19/2006 |Sell |29192 |08/19/2011 |Sell |NULL |NULL |NULL
Any ideas would be MUCH appreciated.

The problem with what you are trying to do, is that you are trying to pivot more than one column at a time. This can be done with unpivot then pivot. Something like this:
WITH CTE
AS
(
SELECT
CAST(CaseNumber AS NVARCHAR(50)) AS CaseNumber
,CAST(DecisionNumber AS NVARCHAR(50)) AS DecisionNumber
,CAST(Date AS NVARCHAR(50)) AS [Date]
,ROW_NUMBER() OVER(PARTITION BY [CaseNumber] ORDER BY DecisionNumber DESC) AS RN
FROM Table1
), unpivoted
AS
(
SELECT CaseNumber, val, col + ' ' + CAST(RN AS NVARCHAR(50)) AS col
FROM CTE
UNPIVOT
(
val
FOR col IN(DecisionNumber, Date)
) AS u
)
SELECT *
FROM unpivoted AS u
PIVOT
(
MAX(val)
FOR col IN([DecisionNumber 1], [Date 1],
[DecisionNumber 2], [Date 2],
[DecisionNumber 3], [Date 3])
) AS p;
SQL Fiddle Demo
This will give you:
| CaseNumber | DecisionNumber 1 | Date 1 | DecisionNumber 2 | Date 2 | DecisionNumber 3 | Date 3 |
|------------|------------------|------------|------------------|------------|------------------|------------|
| 444 | 29833 | 2005-04-05 | 29777 | 2006-05-10 | 29654 | 2007-08-19 |
| 468 | 29230 | 2006-08-19 | 29192 | 2011-08-19 | (null) | (null) |
However, if you want to do this for any number of decisionnumber and date, you can do this:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
WITH CTE
AS
(
SELECT
CAST(CaseNumber AS NVARCHAR(50)) AS CaseNumber
,CAST(DecisionNumber AS NVARCHAR(50)) AS DecisionNumber
,CAST(Date AS NVARCHAR(50)) AS [Date]
,ROW_NUMBER() OVER(PARTITION BY [CaseNumber] ORDER BY DecisionNumber DESC) AS RN
FROM Table1
), Data
AS
(
SELECT col, MAX(RN) AS RN
FROM
(
SELECT RN, col + CAST(RN AS NVARCHAR(50)) AS col
FROM CTE
UNPIVOT
(
val
FOR col IN(DecisionNumber, Date)
) AS u
) AS t
GROUP BY col
)
select #cols = STUFF((SELECT ',' +
QUOTENAME(col)
FROM Data
ORDER BY RN
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT #query = 'WITH CTE
AS
(
SELECT
CAST(CaseNumber AS NVARCHAR(50)) AS CaseNumber
,CAST(DecisionNumber AS NVARCHAR(50)) AS DecisionNumber
,CAST(Date AS NVARCHAR(50)) AS [Date]
,ROW_NUMBER() OVER(PARTITION BY [CaseNumber] ORDER BY DecisionNumber DESC) AS RN
FROM Table1
), unpivoted
AS
(
SELECT CaseNumber, val, col + CAST(RN AS NVARCHAR(50)) AS col
FROM CTE
UNPIVOT
(
val
FOR col IN(DecisionNumber, Date)
) AS u
)
SELECT *
FROM unpivoted AS u
PIVOT
(
MAX(val)
FOR col IN('+ #cols + ')
) AS p;';
EXECUTE(#query);
Dynamic SQL Demo

Related

Select every 10 row as a new column in sql server

I have a SQL Table with data as shown,
Item Qty
------------
A1 59
A2 76
A3 86
A1 12
A2 17
A3 15
A1 23
A2 39
A3 07
Here we can see Item is repeated.So, i would like to group the records by Item.
I want to get the output table look like below
Item Qty1 Qty2 qty3
----------------------------------
A1 59 12 23
A2 76 17 39
A3 86 15 07
Use pivot with ranking function ROW_NUMBER:
WITH CTE
AS
(
SELECT
Item, QTy, ROW_NUMBER() OVER(PARTITION BY ITem ORDER BY Item) AS RN
FROM tablename
)
SELECT Item, [1] AS Qty1, [2] AS Qty2, [3] AS Qty3
FROM CTE
PIVOT
(
SUM(Qty)
FOR rn IN([1], [2], [3])
) AS p;
demo
Results:
| Item | Qty1 | Qty2 | Qty3 |
|------|------|------|------|
| A1 | 59 | 12 | 23 |
| A2 | 39 | 17 | 76 |
| A3 | 86 | 15 | 7 |
If these quantities are not fixed and they are not 3 always, you need to do it dynamically like this:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
DECLARE #colNames AS NVARCHAR(MaX);
select #cols = STUFF((SELECT distinct ',' +
QUOTENAME(CAST(RN AS NVARCHAR(10)))
FROM
(
SELECT
Item, QTy, ROW_NUMBER() OVER(PARTITION BY ITem ORDER BY Item) AS RN
FROM tablename ) as t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
select #colNames = STUFF((SELECT distinct ',' +
'[' + CAST(RN AS NVARCHAR(10)) + '] AS Qty' +
CAST(RN AS NVARCHAR(10))
FROM
(
SELECT
Item, QTy, ROW_NUMBER() OVER(PARTITION BY ITem ORDER BY Item) AS RN
FROM tablename ) as t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT #query = 'WITH CTE
AS
(
SELECT
Item, QTy, ROW_NUMBER() OVER(PARTITION BY ITem ORDER BY Item) AS RN
FROM tablename
)
SELECT Item,' + #colNames + '
FROM CTE
PIVOT
(
SUM(Qty)
FOR rn IN(' + #cols + ')
) AS p;';
execute(#query);
demo
You can use pivot as below:
Select * from (
Select *, Qtys = Concat('Qty', Row_Number() over(partition by Item order by Item))
from #itemdata
) a
pivot (max(qty) for qtys in ([Qty1],[Qty2],[Qty3])) p
For dynamic list you can query as below:
Declare #cols1 varchar(max)
Declare #query nvarchar(max)
Select #cols1 = stuff((select top (select max(cnt) from (select count(*) cnt from #itemdata group by item ) a) ','+
QuoteName(Concat('Qty', Row_Number() over(order by (select Null)))) from
master..spt_values c1, master..spt_values c2 for xml path('')),1,1,'')
Select #query = ' Select * from (
Select *, Qtys = Concat(''Qty'', Row_Number() over(partition by Item order by Item))
from #itemdata
) a
pivot (max(qty) for qtys in (' + #cols1 + ')) p '
Exec sp_executesql #query

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

Simple SQL Query -

I have data in a SQLServer table like this:
ID Name Year Value
-- ---- ---- -----
2 Ted 2013 2000
2 Ted 2012 1000
I need the view syntax to output this:
ID Name Yr1 Value1 Yr2 Value2
-- ---- --- ------ --- ------
2 Ted 2013 2000 2012 1000
No cursors if possible.
Any clues would be greatful.
In SQL Server there are several ways that you can get the result.
If you have a limited number of values, then you can easily hard-code the result. One way you can get the result would be using an aggregate function with a CASE expression:
select d.id,
d.name,
max(case when seq = 1 then year end) year1,
max(case when seq = 1 then value end) value1,
max(case when seq = 2 then year end) year2,
max(case when seq = 2 then value end) value2
from
(
select id, name, year, value,
row_number() over(partition by id order by year desc) seq
from yourtable
) d
group by d.id, d.name;
See SQL Fiddle with Demo. If you want to use the PIVOT function, then I would suggest first unpivoting the data in the year and value columns first. The process of unpivot converts the multiple columns into multiple rows. You can use the UNPIVOT function, but in my example I used CROSS APPLY with a UNION ALL query and the code is:
select t.id, t.name,
col = c.col+cast(seq as varchar(4)),
c.val
from
(
select id, name, year, value,
row_number() over(partition by id order by year desc) seq
from yourtable
) t
cross apply
(
select 'year', t.year union all
select 'value', t.value
) c (col, val)
See SQL Fiddle with Demo. This converts your multiple columns into a slightly different format with multiple rows:
| ID | NAME | COL | VAL |
| 2 | Ted | year1 | 2013 |
| 2 | Ted | value1 | 2000 |
| 2 | Ted | year2 | 2012 |
| 2 | Ted | value2 | 1000 |
You can then apply the PIVOT function on this to get your final desired result:
select id, name, year1, value1, year2, value2
from
(
select t.id, t.name,
col = c.col+cast(seq as varchar(4)),
c.val
from
(
select id, name, year, value,
row_number() over(partition by id order by year desc) seq
from yourtable
) t
cross apply
(
select 'year', t.year union all
select 'value', t.value
) c (col, val)
) d
pivot
(
max(val)
for col in (year1, value1, year2, value2)
) piv;
See SQL Fiddle with Demo. Finally if you have an unknown number of values that you want to transform from rows into columns, then you can use dynamic SQL inside a stored procedure:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col+cast(seq as varchar(4)))
from
(
select row_number() over(partition by id order by year desc) seq
from yourtable
) d
cross apply
(
select 'year', 1 union all
select 'value', 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 id, name,' + #cols + '
from
(
select t.id, t.name,
col = c.col+cast(seq as varchar(4)),
c.val
from
(
select id, name, year, value,
row_number() over(partition by id order by year desc) seq
from yourtable
) t
cross apply
(
select ''year'', t.year union all
select ''value'', t.value
) c (col, val)
) x
pivot
(
max(val)
for col in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo. All versions will give a result:
| ID | NAME | YEAR1 | VALUE1 | YEAR2 | VALUE2 |
| 2 | Ted | 2013 | 2000 | 2012 | 1000 |

Displaying Columns as Rows in SQL Server 2005

I have read dozens of solutions to similar transposition problems as the one I am about to propose but oddly none that exactly mirrors my issue. I am simply trying to flip my rows to columns in a simple dashboard type data set.
The data when pulled from various transaction tables looks like this:
DatePeriod PeriodNumberOverall Transactions Customers Visits
'Jan 2012' 1 100 50 150
'Feb 2012' 2 200 100 300
'Mar 2012' 3 300 200 600
and I want to be able to generate the following:
Jan 2012 Feb 2012 Mar 2012
Transactions 100 200 300
Customers 50 100 200
Visits 150 300 600
The metrics will be static (Transactions, Customers and Visits), but the date periods will be dynamic (IE - more added as months go by).
Again, I have ready many examples leveraging pivot, unpivot, store procedures, UNION ALLs, etc, but nothing where I am not doing any aggregating, just literally transposing the whole output. I have also found an easy way to do this in Visual Studio 2005 using a matrix with an embedded list, but I can't export the final output to excel which is a requirement. Any help would be greatly appreciated.
In order to get the result that you want you need to first UNPIVOT the data and then PIVOT theDatePeriod` Values.
The UNPIVOT will transform the multiple columns of Transactions, Customers and Visits into multiple rows. The other answers are using a UNION ALL to unpivot but SQL Server 2005 was the first year the UNPIVOT function was supported.
The query to unpivot the data is:
select dateperiod,
col, value
from transactions
unpivot
(
value for col in (Transactions, Customers, Visits)
) u
See Demo. This transforms your current columns into multiple rows, so the data looks like the following:
| DATEPERIOD | COL | VALUE |
-------------------------------------
| Jan 2012 | Transactions | 100 |
| Jan 2012 | Customers | 50 |
| Jan 2012 | Visits | 150 |
| Feb 2012 | Transactions | 200 |
Now, since the data is in rows, you can apply the PIVOT function to the DatePeriod column:
select col, [Jan 2012], [Feb 2012], [Mar 2012]
from
(
select dateperiod,
t.col, value, c.SortOrder
from
(
select dateperiod,
col, value
from transactions
unpivot
(
value for col in (Transactions, Customers, Visits)
) u
) t
inner join
(
select 'Transactions' col, 1 SortOrder
union all
select 'Customers' col, 2 SortOrder
union all
select 'Visits' col, 3 SortOrder
) c
on t.col = c.col
) d
pivot
(
sum(value)
for dateperiod in ([Jan 2012], [Feb 2012], [Mar 2012])
) piv
order by SortOrder;
See SQL Fiddle with Demo.
If you have an unknown number of date period's then you will use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(dateperiod)
from transactions
group by dateperiod, PeriodNumberOverall
order by PeriodNumberOverall
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT col, ' + #cols + '
from
(
select dateperiod,
t.col, value, c.SortOrder
from
(
select dateperiod,
col, value
from transactions
unpivot
(
value for col in (Transactions, Customers, Visits)
) u
) t
inner join
(
select ''Transactions'' col, 1 SortOrder
union all
select ''Customers'' col, 2 SortOrder
union all
select ''Visits'' col, 3 SortOrder
) c
on t.col = c.col
) x
pivot
(
sum(value)
for dateperiod in (' + #cols + ')
) p
order by SortOrder'
execute(#query)
See SQL Fiddle with Demo. Both will give the result:
| COL | JAN 2012 | FEB 2012 | MAR 2012 |
-------------------------------------------------
| Transactions | 100 | 200 | 300 |
| Customers | 50 | 100 | 200 |
| Visits | 150 | 300 | 600 |
You need to dynamically create a SQL statement with PIVOT and APPLY operators on the fly and then run that command. If your metrics static(Transactions, Customers and Visits), hence we can use CROSS APPLY operator with VALUES As a Table Source.
For SQL Server2008+
DECLARE #cols nvarchar( max),
#query nvarchar(max)
SELECT #cols =
STUFF((SELECT ',' + QUOTENAME(t.DatePeriod) AS ColName
FROM dbo.test62 t
FOR XML PATH(''), TYPE).value ('.', 'nvarchar(max)'), 1, 1, '')
SET #query =
'SELECT *
FROM (
SELECT t.DatePeriod, COALESCE(o.Transactions, o.Customers, o.Visits) AS PvtVals, o.PvtColumns, o.OrderColumns
FROM dbo.test62 t CROSS APPLY (
VALUES(t.Transactions, NULL, NULL, ''Transaction'', 1),
(NULL, t.Customers, NULL, ''Customers'', 2),
(NULL, NULL, t.Visits, ''Visits'', 3)
) o (Transactions, Customers, Visits, PvtColumns, OrderColumns)
) p
PIVOT
(
MAX(PvtVals) FOR DatePeriod IN (' + #cols + ')
) AS pvt
ORDER BY pvt.OrderColumns '
EXEC(#query)
Result:
PvtColumns Jan 2012 Fed 2012 Mar 2012
Transaction 100 200 300
Customers 50 100 200
Visits 150 300 600
Demo on SQLFiddle
For SQL Server 2005
DECLARE #cols nvarchar( max),
#query nvarchar(max)
SELECT #cols =
STUFF((SELECT ',' + QUOTENAME(t.DatePeriod) AS ColName
FROM dbo.test62 t
FOR XML PATH(''), TYPE).value ('.', 'nvarchar(max)'), 1, 1, '')
SET #query =
'SELECT *
FROM (
SELECT t.DatePeriod, COALESCE(o.Transactions, o.Customers, o.Visits) AS PvtVals, o.PvtColumns, o.OrderColumns
FROM dbo.test62 t CROSS APPLY (
SELECT t.Transactions, NULL, NULL, ''Transaction'', 1
UNION ALL
SELECT NULL, t.Customers, NULL, ''Customers'', 2
UNION ALL
SELECT NULL, NULL, t.Visits, ''Visits'', 3
) o (Transactions, Customers, Visits, PvtColumns, OrderColumns)
) p
PIVOT
(
MAX(PvtVals) FOR DatePeriod IN (' + #cols + ')
) AS pvt
ORDER BY pvt.OrderColumns'
EXEC(#query)
If you can know how many different date period in advance, then you can use fixed query like following:
;with CTE_UNIONTable
as
(
select [DatePeriod],[PeriodNumberOverall],[Transactions] as [value], 'Transactions' as subType from table1
UNION ALL
select [DatePeriod],[PeriodNumberOverall],[Customers] as [value], 'Customers' as subType from table1
UNION ALL
select [DatePeriod],[PeriodNumberOverall],[Visits] as [value], 'Visits' as subType from table1
), CTE_MiddleResult
as
(
select * from CTE_UNIONTable
pivot
(
max(value)
for DatePeriod in ([Jan 2012],[Feb 2012],[Mar 2012])
) as P
)
select SubType, max([Jan 2012]) as [Jan 2012] ,max([Feb 2012]) as [Feb 2012], max([Mar 2012]) as [Feb 2012]
from CTE_MiddleResult
group by SubType
SQL FIDDLE DEMO
If how many date period is unpredictable, then #Alexander already gave the solution, the following code is just a second opinion, instead of using APPLY, using UNION ALL
DECLARE #cols nvarchar( max),
#query nvarchar (max),
#selective nvarchar(max)
SELECT #cols =
STUFF((SELECT ',' + QUOTENAME(t.DatePeriod) AS ColName
FROM table1 t
FOR XML PATH( ''), TYPE).value ('.', 'nvarchar(max)'),1,1,'')
SELECT #selective =
STUFF((SELECT ',MAX(' + QUOTENAME(t.DatePeriod) +') as ' + QUOTENAME(t.DatePeriod) AS ColName
FROM table1 t
FOR XML PATH( ''), TYPE).value ('.', 'nvarchar(max)'),1,1,'')
set #query = '
;with CTE_UNIONTable
as
(
select [DatePeriod],[PeriodNumberOverall],[Transactions] as [value], ''Transactions'' as subType from table1
UNION ALL
select [DatePeriod],[PeriodNumberOverall],[Customers] as [value], ''Customers'' as subType from table1
UNION ALL
select [DatePeriod],[PeriodNumberOverall],[Visits] as [value], ''Visits'' as subType from table1
), CTE_MiddleResult
as
(
select * from CTE_UNIONTable
pivot
(
max(value)
for DatePeriod in ('+#cols+')
) as P
)
select SubType,' + #selective + '
from CTE_MiddleResult
group by SubType'
exec(#query)
SQL FIDDLE DEMO

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.