How to join same records in sql server - sql

I have data like this :
| ID | NAME | PRICE
| 01 | TEST | 5000
| 01 | TEST | 10000
| 02 | EAST | 4500
| 03 | AEST | 5000
| 03 | AEST | 5000
I want to join that same records so the final result is like this :
| ID | NAME | PRICE1 | PRICE2
| 01 | TEST | 5000 | 10000
| 02 | EAST | 4500 | 0
| 03 | AEST | 5000 | 5000

SQL tables represent unordered sets. Given the information you have provided, there is no way to determine the price1 and price2 as you have specified. So, you might as well use MIN() and MAX():
select id, name, min(price) as price1, max(price) as price2
from t
group by id, name;
If you did have a column that specified the ordering, then you could use pivot or conditional aggregation:
select id, name,
max(case when seqnum = 1 then price end) as price1,
max(case when seqnum = 2 then price end) as price2
from (select t.*,
row_number() over (partition by id order by ??) as seqnum
from t
) t
group by id, name;
The ?? is for the column that specifies the ordering.

IF OBJECT_ID('Tempdb..#Temp') IS NOT NULL
Drop table #Temp
;With cte(ID,NAME,PRICE)
AS
(
SELECT 01 , 'TEST' , 5000 UNION ALL
SELECT 01 , 'TEST' , 10000 UNION ALL
SELECT 02 , 'EAST' , 4500 UNION ALL
SELECT 03 , 'AEST' , 5000 UNION ALL
SELECT 03 , 'AEST' , 5000
)
SELECT *, 'Price' + CAST(ROW_NUMBER()Over(PArtition by NAME Order by NAME) AS Varchar(5)) AS PriceCol INTO #Temp FROM cte
DECLARE #Coulmn nvarchar(max),
#Coulmn2 nvarchar(max),
#Sql nvarchar(max)
SELECT #Coulmn=STUFF((SELECT DISTINCT ', '+ '['+ PriceCol +']' From #Temp
FOR XML PATH ('')),1,1,'')
--SELECT #Coulmn
SELECT #Coulmn2=STUFF((SELECT DISTINCT ', '+ 'ISNULL(' + PriceCol + ',''0'')' +' AS ['+PriceCol +']' From #Temp
FOR XML PATH ('')),1,1,'')
--SELECT #Coulmn2
SET #Sql=' SELECT ID,NAME, '+#Coulmn2+ ' From
(
SELECT * From #Temp
)As Src
PIVOT
(
MAX(PRICE) FOR PriceCol IN ('+ #Coulmn +')
)Pvt
Order By Pvt.ID
'
Print #Sql
Exec(#Sql)

Related

Convert rows to column by date with pivot

I have read the stuff on MS pivot tables and I am still having problems getting this correct.
Data
wh_id | saledate | qty |
105 | 20190901 | 134.000000 |
105 | 20190902 | 190.000000 |
105 | 20190903 | 148.500000 |
105 | 20190904 | 157.500000 |
105 | 20190905 | 209.500000 |
I would like it to come out as a pivot table, like this:
wh_id | 1 | 2 | 3 | 4 | 5 |
105 | 134 | 190 |148.5 | 157.5 | 209.5 |
this the code :
DECLARE
#cols nvarchar(max)='' ,
#query nvarchar(max)=''
SET #cols = STUFF((SELECT ',' + QUOTENAME(DATEPART(dd, saledate))
FROM sales
WHERE month(saleDATE)=9 and year(saleDATE)=2019 and wh_id=105
GROUP BY saledate
ORDER BY saledate ASC
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'');
set #query = 'SELECT [wh_id], ' + #cols + '
from
(
select [wh_id], QUOTENAME(DATEPART(dd, saledate)) saledate,qty
from sales where month(saleDATE)=9 and year(saleDATE)=2019 and wh_id=105
) x
pivot
(
sum(qty)
for [saledate] in (' + #cols + ')
) p '
execute(#query);
but the result is like this
wh_id | 1 | 2 | 3 | 4 | 5 |
105 | 1 | 2 | 3 | 4 | 5 |
just change the source part from the above query remove QUOTENAME applied over saledate and try executing the query you will find the expected output.
set #query = 'SELECT [wh_id], ' + #cols + '
from
(
select [wh_id], DATEPART(dd, saledate) saledate,qty
from sales where month(saleDATE)=9 and year(saleDATE)=2019 and wh_id=105
) x
pivot
(
sum(qty)
for [saledate] in (' + #cols + ') ) p '
select wh_id,
max(case when rn = 1 then qty end) '1',
max(case when rn = 2 then qty end) '2',
max(case when rn = 3 then qty end) '3',
max(case when rn = 4 then qty end) '4',
max(case when rn = 5 then qty end) '5'
from
(
select wh_id,qty,
row_number() over(partition by wh_id order by qty) rn
from YourTableName
) src
group by wh_id
OutPut:-
Note:- Instead of using Pivot ...you can use this simple query...using ...case when and then with max aggregate function...
In above example....i'm forgot add decimal value in Table....

Columns to rows and rows to column conversion without pivot/unpivot

All I need is convert Column(A1/A2) into rows and rows(1 into Jan) into columns.
Input:
Here A1/A2 belongs to say A and they are calculated as A1/A2 for each month.
Month A1 A2 B1 B2 C1 C2
1 120 60 40 80 120 120
2 50 50 40 20 60 30
3 50 25 40 10 90 30
I need below o/p without using pivot and unpivot
O/P:
X Jan(1 is denoting Jan) Feb Mar
A 120/60(calculation:A1/A2) 40/80 120/120
B 50/50 40/20 60/30
C 50/25 40/10 90/30
I tried but my query is too long as I am using case and Union All three times each For A1 A2,B1 B2,C1 C2 etc.
Using cross apply(values ...) and conditional aggregation:
select
v.X
, Jan = max(case when t.month=1 then v.Value end)
, Feb = max(case when t.month=2 then v.Value end)
, Mar = max(case when t.month=3 then v.Value end)
from t
cross apply(values
('A',convert(varchar(3),A1)+'/'+convert(varchar(3),A2))
,('B',convert(varchar(3),B1)+'/'+convert(varchar(3),B2))
,('C',convert(varchar(3),C1)+'/'+convert(varchar(3),C2))
) v (X,Value)
group by v.X
rextester demo: http://rextester.com/XICI74484
returns:
+---+---------+-------+-------+
| X | Jan | Feb | Mar |
+---+---------+-------+-------+
| A | 120/60 | 50/50 | 50/25 |
| B | 40/80 | 40/20 | 40/10 |
| C | 120/120 | 60/30 | 90/30 |
+---+---------+-------+-------+
For the evaluation of the expressions:
select
v.X
, Jan = max(case when t.month=1 then v.Value end)
, Feb = max(case when t.month=2 then v.Value end)
, Mar = max(case when t.month=3 then v.Value end)
from t
cross apply(values
('A',(A1*1.0)/A2)
,('B',(B1*1.0)/B2)
,('C',(C1*1.0)/C2)
) v (X,Value)
group by v.X
returns:
+---+------+------+------+
| X | Jan | Feb | Mar |
+---+------+------+------+
| A | 2.00 | 1.00 | 2.00 |
| B | 0.50 | 2.00 | 4.00 |
| C | 1.00 | 2.00 | 3.00 |
+---+------+------+------+
You can use XML. This snippet may be more than you want but it shows how
--------------------------------------------------
-----------------Begin TRANSPOSE-----------------
--------------------------------------------------
DECLARE #xml XML
,#RowCount BIGINT
DECLARE #sSQl NVARCHAR(MAX)= 'SELECT (SELECT DISTINCT ColumnName FROM #TempTable WHERE CellId=Cell.CellId) as ColumnName,'
---------------------------------------------------
--Set up #table Here or outside the code... but #TABLE WILL BE DROPPED AT FINISH
---------------------------------------------------
--#############################################################################
-- the temp table #Table will contain what you want to Transpose
--#############################################################################
--SELECT * INTO #Table FROM Widgets W WHERE WidgetId IN (1096,803)
---------------------------------------------------
---------------- End #Table Setup ---------------
---------------------------------------------------
SET #xml = (SELECT *
,ROW_NUMBER() OVER (ORDER BY (SELECT 1
)) Rn
FROM #Table Row
FOR
XML AUTO
,ROOT('Root')
,ELEMENTS XSINIL
);
WITH RC AS
(SELECT COUNT(Row.value('.', 'nvarchar(MAX)')) [RowCount]
FROM #xml.nodes('Root/Row') AS WTable(Row))
,c AS(
SELECT b.value('local-name(.)','nvarchar(max)') ColumnName,
b.value('.[not(#xsi:nil = "true")]','nvarchar(max)') Value,
b.value('../Rn[1]','nvarchar(max)') Rn,
ROW_NUMBER() OVER (PARTITION BY b.value('../Rn[1]','nvarchar(max)') ORDER BY (SELECT 1)) Cell
FROM
#xml.nodes('//Root/Row/*[local-name(.)!="Rn"]') a(b)
),Cols AS (
SELECT DISTINCT c.ColumnName,
c.Cell
FROM c
)
INSERT INTO #TempTable (CellId,RowID,Value,ColumnName)
SELECT Cell,Rn,Value,REPLACE(c.ColumnName,'_x0023_','#')
FROM c
SELECT #sSQL = #sSQl + '(SELECT T2.Value FROM #Temptable T2 WHERE T2.CellId=Cell.CellID AND T2.Rowid=' + CAST(T.RowId AS NVARCHAR) + ') AS __________________Value____________________'
+ CAST(T.RowID AS NVARCHAR) + ','
FROM (SELECT DISTINCT
RowId
FROM #TempTable
) T
SET #sSQl = LEFT(#sSQL, LEN(#sSQL) - 1) + ' FROM (SELECT DISTINCT CellId FROM #TempTable) Cell order by columnname'
EXECUTE sp_Executesql #sSQl
--here you will have your output
-- PRINT #sSQl
DROP TABLE #Table
DROP TABLE #TempTable
--------------------------------------------------
-----------------END TRANSPOSE-------------------
--------------------------------------------------

SQL Server - Complex Dynamic Pivot multiple columns

FYI, this question is already answered but I have some new requirements which is very complex to implement so, I am posting it as a new question instead of editing the old question: (Previous Question)
I have two tables "Controls" and "ControlChilds" (in the ControlChilds table we have added a new column called ControlChildComments which we need to show in PIVOT output)
Parent Table Structure:
Create table Controls(
ProjectID Varchar(20) NOT NULL,
ControlID INT NOT NULL,
ControlCode Varchar(2) NOT NULL,
ControlPoint Decimal NULL,
ControlScore Decimal NULL,
ControlValue Varchar(50)
)
Sample Data
ProjectID | ControlID | ControlCode | ControlPoint | ControlScore | ControlValue
P001 1 A 30.44 65 Invalid
P001 2 C 45.30 85 Valid
Child Table Structure:
Create table ControlChilds(
ControlID INT NOT NULL,
ControlChildID INT NOT NULL,
ControlChildValue Varchar(200) NULL,
ControlChildComments Varchar(200) NULL
)
Sample Data
ControlID | ControlChildID | ControlChildValue | ControlChildComments
1 100 Yes Something
1 101 No NULL
1 102 NA Others
1 103 Others NULL
2 104 Yes New one
2 105 SomeValue NULL
Based on my previous question (Previous Question) I got this output (You can refer to the PIVOT queries which produces this output in the answer given by #bluefeet. Thanks again #bluefeet.)
But now my requirement is changed and I need ControlChildComments after each Child values. For example, A_Child1, A_Child1Comments, A_Child2, A_Child2Comments etc...
Another tricky thing is I need to show the comments only when they are not null otherwise I shouldn't show the column. For example, in this case, it should be like this:
A_Child1, A_Child1Comments, A_Child2, A_Child3, A_Child3Comments, A_Child4, C_Child1, C_Child1Comments, C_Child2
Is this possible? I tried lot of things but the results are not accurate.
Since you now have multiple columns in your ControlChilds table that you need to PIVOT, you will need to use the similar method of unpivoting them first that you applied with the Controls table.
You will need to unpivot both the ChildControlValue and ChildControlComments using code similar to:
select
projectId,
col = ControlCode+'_'+subCol+cast(seq as varchar(10)),
value
from
(
select c.ProjectId,
c.ControlCode,
cc.ControlChildValue,
cc.ControlChildComments,
row_number() over(partition by c.ProjectId, c.ControlCode
order by cc.ControlChildId) seq
from controls c
inner join controlchilds cc
on c.controlid = cc.controlid
) d
cross apply
(
select 'ChildValue', ControlChildValue union all
select 'ChildComments', ControlChildComments
) c (subCol, value);
See SQL Fiddle with Demo. This gets your data in the format:
| PROJECTID | COL | VALUE |
|-----------|------------------|-----------|
| P001 | A_ChildValue1 | Yes |
| P001 | A_ChildComments1 | Something |
| P001 | A_ChildValue2 | No |
| P001 | A_ChildComments2 | (null) |
| P001 | A_ChildValue3 | NA |
You then use this code in your existing query:
select ProjectId,
A_ControlPoint, A_ControlScore, A_ControlValue,
A_ChildValue1, A_ChildComments1, A_ChildValue2,
A_ChildComments2, A_ChildValue3, A_ChildComments3,
A_ChildValue4, A_ChildComments4,
C_ControlPoint, C_ControlScore, C_ControlValue,
C_Child1, C_Child2
from
(
select
ProjectId,
col = ControlCode +'_'+col,
val
from
(
select
c.ProjectId,
c.ControlCode,
c.ControlPoint,
c.ControlScore,
c.ControlValue
from controls c
) d
cross apply
(
select 'ControlPoint', cast(controlpoint as varchar(10)) union all
select 'ControlScore', cast(ControlScore as varchar(10)) union all
select 'ControlValue', ControlValue
) c (col, val)
union all
select
projectId,
col = ControlCode+'_'+subCol+cast(seq as varchar(10)),
value
from
(
select c.ProjectId,
c.ControlCode,
cc.ControlChildValue,
cc.ControlChildComments,
row_number() over(partition by c.ProjectId, c.ControlCode
order by cc.ControlChildId) seq
from controls c
inner join controlchilds cc
on c.controlid = cc.controlid
) d
cross apply
(
select 'ChildValue', ControlChildValue union all
select 'ChildComments', ControlChildComments
) c (subCol, value)
) src
pivot
(
max(val)
for col in (A_ControlPoint, A_ControlScore, A_ControlValue,
A_ChildValue1, A_ChildComments1, A_ChildValue2,
A_ChildComments2, A_ChildValue3, A_ChildComments3,
A_ChildValue4, A_ChildComments4,
C_ControlPoint, C_ControlScore, C_ControlValue,
C_Child1, C_Child2)
) piv;
See SQL Fiddle with Demo. Finally, you'll implement this in your dynamic SQL script:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col)
from
(
select ControlCode,
col = ControlCode +'_'+col,
seq,
so
from controls
cross apply
(
select 'ControlPoint', 0, 0 union all
select 'ControlScore', 0, 1 union all
select 'ControlValue', 0, 2
) c (col, seq, so)
union all
select ControlCode,
col = ControlCode+'_'+subcol+cast(rn as varchar(10)),
rn,
so
from
(
select ControlCode,
row_number() over(partition by c.ProjectId, c.ControlCode
order by cc.ControlChildId) seq
from controls c
inner join controlchilds cc
on c.controlid = cc.controlid
) d
cross apply
(
select 'ChildValue', seq, 3 union all
select 'ChildComments', seq, 4
) c (subcol, rn, so)
) src
group by ControlCode, seq, col, so
order by ControlCode, seq, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ProjectId, ' + #cols + '
from
(
select ProjectId,
col = ControlCode +''_''+col,
val
from
(
select
c.ProjectId,
c.ControlCode,
c.ControlPoint,
c.ControlScore,
c.ControlValue
from controls c
) d
cross apply
(
select ''ControlPoint'', cast(controlpoint as varchar(10)) union all
select ''ControlScore'', cast(ControlScore as varchar(10)) union all
select ''ControlValue'', ControlValue
) c (col, val)
union all
select
projectId,
col = ControlCode+''_''+subCol+cast(seq as varchar(10)),
value
from
(
select c.ProjectId,
c.ControlCode,
cc.ControlChildValue,
cc.ControlChildComments,
row_number() over(partition by c.ProjectId, c.ControlCode
order by cc.ControlChildId) seq
from controls c
inner join controlchilds cc
on c.controlid = cc.controlid
) d
cross apply
(
select ''ChildValue'', ControlChildValue union all
select ''ChildComments'', ControlChildComments
) c (subCol, value)
) x
pivot
(
max(val)
for col in (' + #cols + ')
) p '
exec sp_executesql #query;
See SQL Fiddle with Demo. Both of these gives a result:
| PROJECTID | A_CONTROLPOINT | A_CONTROLSCORE | A_CONTROLVALUE | A_CHILDVALUE1 | A_CHILDCOMMENTS1 | A_CHILDVALUE2 | A_CHILDCOMMENTS2 | A_CHILDVALUE3 | A_CHILDCOMMENTS3 | A_CHILDVALUE4 | A_CHILDCOMMENTS4 | C_CONTROLPOINT | C_CONTROLSCORE | C_CONTROLVALUE | C_CHILDVALUE1 | C_CHILDCOMMENTS1 | C_CHILDVALUE2 | C_CHILDCOMMENTS2 |
|-----------|----------------|----------------|----------------|---------------|------------------|---------------|------------------|---------------|------------------|---------------|------------------|----------------|----------------|----------------|---------------|------------------|---------------|------------------|
| P001 | 30.44 | 65.00 | Invalid | Yes | Something | No | (null) | NA | Others | Others | (null) | 45.30 | 85.00 | Valid | Yes | New one | SomeValue | (null) |
Here is an example of a dynamic crosstab. Since you have multiple columns you would need to adjust the dynamic portion of this to suit.
if OBJECT_ID('Something') is not null
drop table Something
create table Something
(
ID int,
Subject1 varchar(50)
)
insert Something
select 10868952, 'NUR/3110/D507' union all
select 10868952, 'NUR/3110/D512' union all
select 10868952, 'NUR/4010/D523' union all
select 10868952, 'NUR/4010/HD20' union all
select 12345, 'asdfasdf'
declare #MaxCols int
declare #StaticPortion nvarchar(2000) =
'with OrderedResults as
(
select *, ROW_NUMBER() over(partition by ID order by Subject1) as RowNum
from Something
)
select ID';
declare #DynamicPortion nvarchar(max) = '';
declare #FinalStaticPortion nvarchar(2000) = ' from OrderedResults Group by ID order by ID';
with E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select #DynamicPortion = #DynamicPortion +
', MAX(Case when RowNum = ' + CAST(N as varchar(6)) + ' then Subject1 end) as Subject' + CAST(N as varchar(6)) + CHAR(10)
from cteTally t
where t.N <=
(
select top 1 Count(*)
from Something
group by ID
order by COUNT(*) desc
)
select #StaticPortion + #DynamicPortion + #FinalStaticPortion
--declare #SqlToExecute nvarchar(max) = #StaticPortion + #DynamicPortion + #FinalStaticPortion;
--exec sp_executesql #SqlToExecute

SQL Server Rows to Multi-Columns

I have the following table and data:
CREATE TABLE SourceTbl ([Code] varchar(3), [Total] decimal, [Date] datetime );
INSERT INTO SourceTbl ([Code], [Total], [Date])
VALUES ('AA', 100, '2012-12-01'), ('AA', 200, '2013-02-01'), ('BB', 50, '2012-01-01');
A simple select will return
Code | Total | Date
'AA' | 100 | 2012-12-01
'AA' | 200 | 2013-02-01
'BB' | 50 | 2012-01-01
but what I need is the following
Code | Total | Date | Total | Date
'AA | 200 | 2013-02-01 | 100 | 2012-12-01
'BB | 50 | 2012-01-01 | null | null
I have been trying to do this using a PIVOT operator but without success (based on the question SQL Server Pivot multiple columns based on one column).
Using that example, all I get are two rows with null values.
The Total/Date columns can be repeated 13 times and they must be ordered by Date DESC.
SQL Fiddle: http://sqlfiddle.com/#!3/f37a1/2
Any help is appreciated!
Thanks!
If you need just two columns:
with cte as (
select *, row_number() over(partition by Code order by Date) as rn
from SourceTbl
)
select
code,
max(case when rn = 1 then Total end) as Total1,
max(case when rn = 1 then Date end) as Date1,
max(case when rn = 2 then Total end) as Total2,
max(case when rn = 2 then Date end) as Date2
from cte
group by code
=> sql fiddle demo
dynamic solution:
declare #stmt nvarchar(max)
;with cte as (
select distinct
cast(row_number() over(partition by Code order by Date) as nvarchar(max)) as rn
from SourceTbl
)
select #stmt = isnull(#stmt + ', ', '') +
'max(case when rn = ' + rn + ' then Total end) as Total' + rn + ',' +
'max(case when rn = ' + rn + ' then Date end) as Date' + rn
from cte
order by rn
select #stmt = '
with cte as (
select *, row_number() over(partition by Code order by Date) as rn
from SourceTbl
)
select
code, ' + #stmt + ' from cte group by code'
exec sp_executesql
#stmt = #stmt
=> sql fiddle demo
Are you trying to dynamically create columns in your result set?
If you had a third record of 'AA' with a total of 300 and the date of 03/01/2013 would you that mean you would want something like this displayed?
Code | Total | Date | Total | Date | Total | Date
AA | 200 | 2013-02-01 | 100 | 2012-12-01| 300 | 03-01-13
BB | 50 | 2012-01-01 | null | null | null | null

SQL Server : dynamic pivot over 5 columns

I'm having a very tough time trying to figure out how to do a dynamic pivot in SQL Server 2008 with multiple columns.
My sample table is as follows:
ID YEAR TYPE TOTAL VOLUME
DD1 2008 A 1000 10
DD1 2008 B 2000 20
DD1 2008 C 3000 30
DD1 2009 A 4000 40
DD1 2009 B 5000 50
DD1 2009 C 6000 60
DD2 2008 A 7000 70
DD2 2008 B 8000 80
DD2 2008 C 9000 90
DD2 2009 A 10000 100
DD2 2009 B 11000 110
DD2 2009 C 12000 120
and I'm trying the pivot it as follows:
ID 2008_A_TOTAL 2008_A_VOLUME 2008_B_TOTAL 2008_B_VOLUME 2008_C_TOTAL 2008_C_VOLUME 2009_A_TOTAL 2009_A_VOLUME 2009_B_TOTAL 2009_B_VOLUME 2009_C_TOTAL 2009_C_VOLUME
DD1 1000 10 2000 20 3000 30 4000 40 5000 50 6000 60
DD2 7000 70 8000 80 9000 90 10000 100 11000 110 12000 120
My SQL Server 2008 query is as follows to create the table:
CREATE TABLE ATM_TRANSACTIONS
(
ID varchar(5),
T_YEAR varchar(4),
T_TYPE varchar(3),
TOTAL int,
VOLUME int
);
INSERT INTO ATM_TRANSACTIONS
(ID,T_YEAR,T_TYPE,TOTAL,VOLUME)
VALUES
('DD1','2008','A',1000,10),
('DD1','2008','B',2000,20),
('DD1','2008','C',3000,30),
('DD1','2009','A',4000,40),
('DD1','2009','B',5000,50),
('DD1','2009','C',6000,60),
('DD2','2008','A',7000,70),
('DD2','2008','B',8000,80),
('DD2','2008','C',9000,90),
('DD2','2009','A',10000,100),
('DD2','2009','B',11000,110),
('DD2','2009','C',1200,120);
The T_Year column may change in the future but the T_TYPE column is generally know, so I'm not sure if I can use a combination of the PIVOT function in SQL Server with dynamic code?
I tried following the example here:
http://social.technet.microsoft.com/wiki/contents/articles/17510.t-sql-dynamic-pivot-on-multiple-columns.aspx
but I ended up with with weird results.
In order to get the result, you will need to look at unpivoting the data in the Total and Volume columns first before applying the PIVOT function to get the final result. My suggestion would be to first write a hard-coded version of the query then convert it to dynamic SQL.
The UNPIVOT process converts these multiple columns into rows. There are a few ways to UNPIVOT, you can use the UNPIVOT function or you can use CROSS APPLY. The code to unpivot the data will be similar to:
select id,
col = cast(t_year as varchar(4))+'_'+t_type+'_'+col,
value
from ATM_TRANSACTIONS t
cross apply
(
select 'total', total union all
select 'volume', volume
) c (col, value);
This gives you data in the format:
+-----+---------------+-------+
| id | col | value |
+-----+---------------+-------+
| DD1 | 2008_A_total | 1000 |
| DD1 | 2008_A_volume | 10 |
| DD1 | 2008_B_total | 2000 |
| DD1 | 2008_B_volume | 20 |
| DD1 | 2008_C_total | 3000 |
| DD1 | 2008_C_volume | 30 |
+-----+---------------+-------+
Then you can apply the PIVOT function:
select ID,
[2008_A_total], [2008_A_volume], [2008_B_total], [2008_B_volume],
[2008_C_total], [2008_C_volume], [2009_A_total], [2009_A_volume]
from
(
select id,
col = cast(t_year as varchar(4))+'_'+t_type+'_'+col,
value
from ATM_TRANSACTIONS t
cross apply
(
select 'total', total union all
select 'volume', volume
) c (col, value)
) d
pivot
(
max(value)
for col in ([2008_A_total], [2008_A_volume], [2008_B_total], [2008_B_volume],
[2008_C_total], [2008_C_volume], [2009_A_total], [2009_A_volume])
) piv;
Now that you have the correct logic, you can convert this to dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(cast(t_year as varchar(4))+'_'+t_type+'_'+col)
from ATM_TRANSACTIONS t
cross apply
(
select 'total', 1 union all
select 'volume', 2
) c (col, so)
group by col, so, T_TYPE, T_YEAR
order by T_YEAR, T_TYPE, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT id,' + #cols + '
from
(
select id,
col = cast(t_year as varchar(4))+''_''+t_type+''_''+col,
value
from ATM_TRANSACTIONS t
cross apply
(
select ''total'', total union all
select ''volume'', volume
) c (col, value)
) x
pivot
(
max(value)
for col in (' + #cols + ')
) p '
execute sp_executesql #query;
This will give you a result:
+-----+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+
| id | 2008_A_total | 2008_A_volume | 2008_B_total | 2008_B_volume | 2008_C_total | 2008_C_volume | 2009_A_total | 2009_A_volume | 2009_B_total | 2009_B_volume | 2009_C_total | 2009_C_volume |
+-----+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+
| DD1 | 1000 | 10 | 2000 | 20 | 3000 | 30 | 4000 | 40 | 5000 | 50 | 6000 | 60 |
| DD2 | 7000 | 70 | 8000 | 80 | 9000 | 90 | 10000 | 100 | 11000 | 110 | 1200 | 120 |
+-----+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+
declare #stmt nvarchar(max)
select #stmt = isnull(#stmt + ', ', '') +
'sum(case when T_YEAR = ''' + T.T_YEAR + ''' and T_TYPE = ''' + T.T_TYPE + ''' then TOTAL else 0 end) as ' + quotename(T.T_YEAR + '_' + T.T_TYPE + '_TOTAL') + ',' +
'sum(case when T_YEAR = ''' + T.T_YEAR + ''' and T_TYPE = ''' + T.T_TYPE + ''' then VOLUME else 0 end) as ' + quotename(T.T_YEAR + '_' + T.T_TYPE + '_VOLUME')
from (select distinct T_YEAR, T_TYPE from ATM_TRANSACTIONS) as T
order by T_YEAR, T_TYPE
select #stmt = '
select
ID, ' + #stmt + ' from ATM_TRANSACTIONS group by ID'
exec sp_executesql
#stmt = #stmt
unfortunately, sqlfiddle.com is not working at the moment, so I cannot create an example for you.
The query created by dynamic SQL would be:
select
ID,
sum(case when T_YEAR = '2008' and T_TYPE = 'A' then TOTAL else 0 end) as 2008_A_TOTAL,
sum(case when T_YEAR = '2008' and T_TYPE = 'A' then VOLUME else 0 end) as 2008_A_VOLUME,
...
from ATM_TRANSACTIONS
group by ID
Please try:
DECLARE #pivv NVARCHAR(MAX),#Query NVARCHAR(MAX)
SELECT #pivv=COALESCE(#pivv+',','')+ QUOTENAME(T_YEAR+'_'+T_TYPE+'_TOTAL')+','+QUOTENAME(T_YEAR+'_'+T_TYPE+'_VOLUME') from ATM_TRANSACTIONS GROUP BY T_YEAR, T_TYPE
IF ISNULL(#pivv, '')<>''
SET #Query='SELECT * FROM(
SELECT ID, T_YEAR+''_''+T_TYPE+''_TOTAL'' TYP, TOTAL VAL from ATM_TRANSACTIONS UNION
SELECT ID, T_YEAR+''_''+T_TYPE+''_VOLUME'' TYP, VOLUME VAL from ATM_TRANSACTIONS
)x pivot (SUM(VAL) for TYP in ('+#pivv+')) as xx'
IF ISNULL(#Query, '')<>''
EXEC (#Query)