merge rows in a sql query and create additional columns for different information - sql

I have a sql query that is returning a number of customer results.
each customer has a unique ID but may have multiple transactions.
instead of having multiple rows for each customer i would like to have one row and generate multiple columns for the different info.
for example i have:
id | name | purchase price | date
3 | tim | 20 | 08-2-2014
3 | tim | 25 | 08-5-2014
and i want it to read
id | name | purchase price 1 | date 1 | purchase price 2 | date 2
3 | tim | 20 | 08-2-2014 | 25 | 08-5-2014
I have no idea where to start.

This is done using a dynamic cross tab.
For reference: http://www.sqlservercentral.com/articles/Crosstab/65048/
-- build sample data
create table #temp_table(
id int,
name varchar(255),
[purchase price] decimal(10,2),
[date] smalldatetime
)
insert into #temp_table
select 3, 'tim', 20, '20140802' union all
select 3, 'tim', 25, '20140805' union all
select 4, 'joe', 25, '20140801' union all
select 4, 'joe', 25, '20140803' union all
select 4, 'joe', 25, '20140807' union all
select 1, 'bob', 25, '20140811'
declare #sql1 varchar(4000)
declare #sql2 varchar(4000)
declare #sql3 varchar(4000)
declare #max int
declare #counter int
select #counter = 1
select top 1 #max = count(*) from #temp_table group by id order by count(*) desc-- get number of columns
select #sql1 =
'select
id,
name,
'
select #sql3 =
'from(
select
*,
rn = row_number() over(partition by id, name order by [date])
from #temp_table
)t
group by id, name
order by id'
select #sql2 = ''
while #counter <= #max begin
select #sql2 =
#sql2
+ ' max(case when rn = ' + convert(varchar(10),#counter) + ' then [purchase price] else null end) as [purchase price ' + convert(varchar(10),#counter) + '],' + char(10)
+ ' max(case when rn = ' + convert(varchar(10),#counter) + ' then [date] else null end) as [date ' + convert(varchar(10),#counter) + ']'
if #counter <> #max begin
select #sql2 = #sql2 + ','
end
select #sql2 = #sql2 + char(10)
select #counter = #counter + 1
end
print (#sql1 + #sql2 + #sql3)
exec (#sql1 + #sql2 + #sql3)
drop table #temp_table
Sample Data:
id name purchase price date
----------------------------------------
3 tim 20.00 2014-08-02 00:00:00
3 tim 25.00 2014-08-05 00:00:00
4 joe 25.00 2014-08-01 00:00:00
4 joe 25.00 2014-08-03 00:00:00
4 joe 25.00 2014-08-07 00:00:00
1 bob 25.00 2014-08-11 00:00:00
Result:
id name purchase price 1 date 1 purchase price 2 date 2 purchase price 3 date 3
--------------------------------------------------------------------------------------------------------------------------------------------
1 bob 25.00 2014-08-11 00:00:00 NULL NULL NULL NULL
3 tim 20.00 2014-08-02 00:00:00 25.00 2014-08-05 00:00:00 NULL NULL
4 joe 25.00 2014-08-01 00:00:00 25.00 2014-08-03 00:00:00 25.00 2014-08-07 00:00:00

Related

How to use pivot in two or more columns in SQL Server

This is my sample table
Item Stocks Sold Year
------------------------------------------------------
Shoes 30 5 2018
Slippers 15 15 2019
Sandals 20 10 2016
Pants 25 5 2018
Shoes 20 0 2017
What I'm trying to achieve is that the columns of Stocks and Sold will be posted by Year.
Example output :-
As of 2016 | As of 2017 | As of 2018 | As of 2019
----------------+------+--------+------+--------+------+---------+-------
Item Stocks | Sold | Stocks | Sold | Stocks | Sold | Stocks | Sold
----------------+------+--------+------+--------+------+---------+--------
Shoes | | 20 | 0 | 30 | 5 | |
Slippers | | | | | | 15 | 15
Sandals 20 | 10 | | | | | |
Pants | | | | 25 | 5 | |
Glad for any help :)
Try this:-
declare #Stock nvarchar(max)
(Select #Stock = Stuff( (select distinct ',[Stock_' + cast(year as varchar(5)) + ']' from Stock for xml path('')), 1,1,''))
declare #Sold nvarchar(max)
(Select #Sold = Stuff( (select distinct ',[Sold_' + cast(year as varchar(5)) + ']' from Stock for xml path('')), 1,1,''))
declare #col1 nvarchar(max)
(Select #col1 = Stuff( (select distinct ',cts.[Stock_' + cast(year as varchar(5)) + ']' + ',ctd.[Sold_' + cast(year as varchar(5)) + ']' from Stock for xml path('')), 1,1,''))
declare #query nvarchar(max) = '
; with cte as (
select Item, stock, ''Stock'' + ''_'' + Cast(year as varchar(10)) as colstock from stock)
, ct as (
select Item, Sold, ''Sold'' + ''_'' + Cast(year as varchar(10)) as colsold from stock)
, ctstock as (
select * from
( select item, stock, colstock from cte )
as d
pivot
( max(stock) for colstock in (' + #Stock + ') )
as p
)
, ctsold as (
select * from
( select item, sold, colsold from ct )
as d
pivot
( max(sold) for colsold in (' + #Sold + ') )
as p
)
select cts.item, ' + #col1 + ' from ctstock as cts inner join ctsold as ctd on cts.item = ctd.item'
exec sp_executesql #query
use conditional aggregate
SELECT Item,
SUM (CASE WHEN Year = 2016 THEN Stocks END) as Stock2016,
SUM (CASE WHEN Year = 2016 THEN Sold END) as Sold2016
FROM yourtable
GROUP BY Item

SQL Count Report Query

I have following table in SQL Server.
Test.
id | Author| Ext
----+-------+--------
01 | Bill | txt
02 | Tom | pdf
03 | Bill | doc
04 | Alex | txt
05 | Alex | pdf
06 | Tom | pdf
I want following to be output based on id.
| Author| txt | pdf | doc
--------+-----+-----+-----
| Bill | 1 | 0 | 1
| Tom | 0 | 2 | 0
| Alex | 1 | 1 | 0
Can anyone suggest me query for the same?
Use CASE expression.
Query
select [Author],
sum(case [Ext] when 'txt' then 1 else 0 end) as [txt],
sum(case [Ext] when 'pdf' then 1 else 0 end) as [pdf],
sum(case [Ext] when 'doc' then 1 else 0 end) as [doc]
from [your_table_name]
group by [Author];
If you want Ext column value to be dynamic, you can use dynamic query string.
Example :
CREATE TABLE Table1
(id int, author varchar(100), ext varchar(3));
INSERT INTO Table1
(id, author, ext)
VALUES
(1, 'Bill', 'txt'),
(2, 'Tom', 'pdf'),
(3, 'Bill', 'doc'),
(4, 'Alex', 'txt'),
(5, 'Alex', 'pdf'),
(6, 'Tom', 'pdf')
Query 1:
DECLARE #MyCursor CURSOR
DECLARE #SQLString NVARCHAR(MAX)
DECLARE #MyField VARCHAR(30)
SET #SQLString = 'SELECT author '
SET #MyCursor = CURSOR FOR
SELECT ext FROM Table1 group by ext
OPEN #MyCursor
FETCH NEXT FROM #MyCursor
INTO #MyField
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQLString = #SQLString + ',sum(case [ext] when ''' + #MyField + ''' then 1 else 0 end) as ' + #MyField
FETCH NEXT FROM #MyCursor
INTO #MyField
END
CLOSE #MyCursor
DEALLOCATE #MyCursor
SET #SQLString = #SQLString + ' FROM Table1 group by author'
EXECUTE sp_executesql #SQLString
Results:
| author | doc | pdf | txt |
|--------|-----|-----|-----|
| Alex | 0 | 1 | 1 |
| Bill | 1 | 0 | 1 |
| Tom | 0 | 2 | 0 |
Use below pivot method :
CREATE TABLE #Temp ( id VARCHAR(10) , Author VARCHAR(10), Ext VARCHAR(10) )
INSERT INTO #Temp ( id , Author , Ext )
SELECT 01,'Bill','txt' UNION ALL
SELECT 02,'Tom','pdf' UNION ALL
SELECT 03,'Bill','doc' UNION ALL
SELECT 04,'Alex','txt' UNION ALL
SELECT 05,'Alex','pdf' UNION ALL
SELECT 06,'Tom','pdf'
SELECT *
FROM
(
SELECT Author , Ext
FROM #Temp
) A
PIVOT
(
COUNT(Ext) FOR Ext IN ([txt],[pdf],[doc])
) A
Using PIVOT
with a as
(select 1 as a_id,'abc' as author,'txt' as ext from dual
union
select 2 ,'pqr' as author,'pdf' as ext from dual
union
select 3,'abc' as author,'doc' as ext from dual
union
select 4,'lmn' as author,'txt' as ext from dual
union
select 5,'pqr' as author,'pdf' as ext from dual
)
select *
from
( select *
from a
) pivot
( count(ext)
for ext in
('txt' as txt_ext,'pdf' as pdf_ext,'doc' as doc_ext)
);
For this option, i think using pivot is the best way;
Select * FROM(
Select Authot,Ext,Count(Ext) CountExt FROM #temp Group By Authot,Ext
) t
PIVOT(
SUM(CountExt)
FOR EXt IN ([pdf],[txt],[doc])) as pvt
CASE expression should be used as follow:
SELECT
author,
COUNT(CASE WHEN ext = 'txt' THEN 1 END) as txt,
COUNT(CASE WHEN ext = 'pdf' THEN 1 END) as pdf,
COUNT(CASE WHEN ext = 'doc' THEN 1 END) as doc
FROM myTable
GROUP BY author

CrossTab Query / Pivot Table in MS SQL?

I have a table that has the follow data structure:
terminal | load_time_mns | vehicle
_________________________________________
Terminal 1 | 3 | AA
Terminal 2 | 10 | AF
Terminal 1 | 1 | BF
Terminal 6 | 3 | QRS
Terminal 6 | 1.4 | AA
Terminal 3 | 2.5 | OP
I am trying to get an interval breakdown of load time from each terminal.For example, for the above table, I am trying to create a breakdown that looks like the following:
terminal | [0-1 mns] | [1-2 mns] | [2-3 mns] |
_______________________________________________________________
Terminal 1 | 0 | 1 | 1
_______________________________________________________________
Terminal 2 | 0 | 0 | 0
_______________________________________________________________
Terminal 3 | 0 | 0 | 1
_______________________________________________________________
Terminal 6 | 0 | 1 | 1
After a bit of Googling, it looks like I should be focusing on the pivot() function and crosstab queries. I am reading up on those two, but am still not quite able to get
Something like this might help:
Query 1:
SELECT
terminal,
count(CASE WHEN load_time_mns >= 0 AND load_time_mns < 1 THEN 1 END) [0-1 mns],
count(CASE WHEN load_time_mns >= 1 AND load_time_mns < 2 THEN 1 END) [1-2 mns],
count(CASE WHEN load_time_mns >= 2 AND load_time_mns < 3 THEN 1 END) [2-3 mns]
FROM t
GROUP BY terminal
Results:
| TERMINAL | 0-1 MNS | 1-2 MNS | 2-3 MNS |
|------------|---------|---------|---------|
| Terminal 1 | 0 | 1 | 0 |
| Terminal 2 | 0 | 0 | 0 |
| Terminal 3 | 0 | 0 | 1 |
| Terminal 6 | 0 | 1 | 0 |
Fiddle here.
Note that in your example you did not include 1 in the [0-1] range but you did include 3 in the [0-3] range, which seems not right.
You can use a bit of dynamic SQL to extend this to the complete result set. Create a table called intervals to store the intervals:
Create Table ex (
terminal varchar(10),
load_time_mns decimal(10, 2),
vehicle varchar(3)
);
Insert Into ex values
('Terminal 1', 3, 'AA'),
('Terminal 2', 10, 'AF'),
('Terminal 1', 1, 'BF'),
('Terminal 6', 3, 'QRS'),
('Terminal 6', 1.4, 'AA'),
('Terminal 3', 2.5, 'OP');
Create Table intervals (
min_mns decimal(10, 2),
max_mns decimal(10, 2),
column_name sysname
);
declare #i int = 0
while #i <= 20
begin
insert into intervals values (
#i, #i + 1, convert(varchar, #i) + '-' + convert(varchar, #i + 1)
);
set #i += 1;
end
while #i <= 420
begin
insert into intervals values (
#i, #i + 5, convert(varchar, #i) + '-' + convert(varchar, #i + 5)
);
set #i += 5;
end
You can then use a cursor to build up the complete SQL
declare
#sql nvarchar(max) = N'select terminal',
#lo int, #hi int, #col sysname;
declare pivot_cursor cursor local fast_forward for
select
min_mns, max_mns, column_name
from
intervals
order by
min_mns;
open pivot_cursor;
fetch next from pivot_cursor into #lo, #hi, #col;
while ##fetch_status = 0
begin
set #sql += ', sum(case when load_time_mns >= ' + convert(varchar, #lo)
+ ' and load_time_mns < ' + convert(varchar, #hi)
+ ' then 1 else 0 end) as [' + #col + ']';
fetch next from pivot_cursor into #lo, #hi, #col;
end
close pivot_cursor;
deallocate pivot_cursor;
Set #sql += ' from ex group by Terminal order by terminal';
exec sp_executesql #sql;
Example SQLFiddle
DECLARE #t TABLE ( terminal VARCHAR(10), load_time_mns DECIMAL(5,2), vehicle VARCHAR(3))
INSERT INTO #t ( terminal, load_time_mns, vehicle )
VALUES
('Terminal 1' , 3 , 'AA'),
('Terminal 2' , 10 , 'AF'),
('Terminal 2' , 20 , 'AF'),
('Terminal 1' , 1 , 'BF'),
('Terminal 1' , 25 , 'BF'),
('Terminal 6' , 3 , 'QRS'),
('Terminal 6' , 1.4 , 'AA'),
('Terminal 3' , 2.5 , 'OP')
;WITH intervals AS
(
SELECT m = 0, n = 1
UNION ALL
SELECT CASE WHEN m < 20 THEN m+1 ELSE m+5 END, CASE WHEN n < 20 THEN n+1 ELSE n+5 END
FROM intervals
WHERE n<420
)
SELECT terminal, load_time_mns, vehicle,
interval = CAST(m AS VARCHAR(10)) + '-' + CAST(n AS VARCHAR(10)), m
INTO ##tmp
FROM intervals i
LEFT JOIN #t t
ON t.load_time_mns >= i.m
AND t.load_time_mns < i.n
OPTION (MAXRECURSION 0)
DECLARE #cols VARCHAR(MAX) =
STUFF(CAST((SELECT ',' + QUOTENAME(interval)
FROM (
SELECT DISTINCT interval, m
FROM ##tmp
) t
ORDER BY m
FOR XML PATH(''), TYPE
) AS VARCHAR(MAX)),1,1,'')
DECLARE #sql VARCHAR(MAX) = '
SELECT terminal, ' + #cols + '
FROM (
SELECT terminal, vehicle, interval
FROM ##tmp
) t
PIVOT (
COUNT(vehicle)
FOR interval IN (' + #cols + ')
) p
'
EXEC(#sql)
DROP TABLE ##tmp

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)