extend current query, calculated columns - sql

My table looks for example like this:
Name date result
A 2012-01-01 1
A 2012-02-01 2
B 2013-01-01 1
...
For a full example: http://sqlfiddle.com/#!3/0226b/1
At the moment I have a working query that counts the rows by person and year: http://sqlfiddle.com/#!3/0226b/3
This is perfect, but what I want is some extra information for 2014. i need to count how many rows I have for every result.
something like this:
NAME 1 2 3 2014 2013 2012 TOTAL
Person B 4 0 2 6 2 2 10
Person A 2 1 1 4 3 4 11
Person C 1 1 1 3 1 0 4
Even better would be that I give the result-columns a good name (1 = lost, 2= draw, 3=won):
NAME lost draw won 2014 2013 2012 TOTAL
Person B 4 0 2 6 2 2 10
Person A 2 1 1 4 3 4 11
Person C 1 1 1 3 1 0 4
I tried to add some extra code, like:
select #colsResult
= STUFF((SELECT ',' + QUOTENAME(result)
from list
group by result
order by result
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
I have as result:
,[1]
,[2]
,[3]
But if I run the whole code I get an error, invallid column name...

Since you have two columns that you now want to PIVOT, you'll first have to unpivot those columns and then convert those values into the new columns.
Starting in SQL Server 2005, you could use CROSS APPLY to unpivot the columns. The basic syntax will be similar to:
select
name,
new_col,
total
from
(
select name,
dt = year(date),
result,
total = count(*) over(partition by name)
from list
) d
cross apply
(
select 'dt', dt union all
select 'result', result
) c (old_col_name, new_col)
See SQL Fiddle with Demo. This query gets you a list of names, with the "new columns" and then the Total entries for each name.
| NAME | NEW_COL | TOTAL |
|----------|---------|-------|
| Person A | 2012 | 11 |
| Person A | 1 | 11 |
| Person A | 2012 | 11 |
| Person A | 2 | 11 |
You'll see that the dates and the results are now both stored in "new_col". These values will now be used as the new column names. If you have a limited number of columns, then you would simply hard-code the query:
select name, lost = [1],
draw=[2], won = [3],
[2014], [2013], [2012], Total
from
(
select
name,
new_col,
total
from
(
select name,
dt = year(date),
result,
total = count(*) over(partition by name)
from list
) d
cross apply
(
select 'dt', dt union all
select 'result', result
) c (old_col_name, new_col)
) src
pivot
(
count(new_col)
for new_col in([1], [2], [3], [2014], [2013], [2012])
) piv
order by [2014];
See SQL Fiddle with Demo
Now since your years are dynamic, then you'll need to use dynamic sql. But it appears that you have 3 results and potentially multiple years - so I'd use a combination of static/dynamic sql to make this easier:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#orderby nvarchar(max)
select #cols
= STUFF((SELECT ',' + QUOTENAME(year(date))
from list
group by year(date)
order by year(date) desc
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #orderby = 'ORDER BY ['+cast(year(getdate()) as varchar(4)) + '] desc'
set #query = 'SELECT name, lost = [1],
draw=[2], won = [3],' + #cols + ', Total
from
(
select
name,
new_col,
total
from
(
select name,
dt = year(date),
result,
total = count(*) over(partition by name)
from list
) d
cross apply
(
select ''dt'', dt union all
select ''result'', result
) c (old_col_name, new_col)
) x
pivot
(
count(new_col)
for new_col in ([1], [2], [3],' + #cols + ')
) p '+ #orderby
exec sp_executesql #query;
See SQL Fiddle with Demo. This gives a result:
| NAME | LOST | DRAW | WON | 2014 | 2013 | 2012 | TOTAL |
|----------|------|------|-----|------|------|------|-------|
| Person B | 7 | 1 | 2 | 6 | 2 | 2 | 10 |
| Person A | 5 | 3 | 3 | 4 | 3 | 4 | 11 |
| Person C | 2 | 1 | 1 | 3 | 1 | 0 | 4 |
If you want to only filter the result columns for the current year, then you can perform this filtering a variety of ways but the easiest you be to include a filter in the unpivot. The hard-coded version would be:
select name, lost = [1],
draw=[2], won = [3],
[2014], [2013], [2012], Total
from
(
select
name,
new_col,
total
from
(
select name,
dt = year(date),
result,
total = count(*) over(partition by name)
from list
) d
cross apply
(
select 'dt', dt union all
select 'result', case when dt = 2014 then result end
) c (old_col_name, new_col)
) src
pivot
(
count(new_col)
for new_col in([1], [2], [3], [2014], [2013], [2012])
) piv
order by [2014] desc;
See SQL Fiddle with Demo. Then the dynamic sql version would be:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#orderby nvarchar(max),
#currentYear varchar(4)
select #currentYear = cast(year(getdate()) as varchar(4))
select #cols
= STUFF((SELECT ',' + QUOTENAME(year(date))
from list
group by year(date)
order by year(date) desc
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #orderby = 'ORDER BY ['+ #currentYear + '] desc'
set #query = 'SELECT name, lost = [1],
draw=[2], won = [3],' + #cols + ', Total
from
(
select
name,
new_col,
total
from
(
select name,
dt = year(date),
result,
total = count(*) over(partition by name)
from list
) d
cross apply
(
select ''dt'', dt union all
select ''result'', case when dt = '+#currentYear+' then result end
) c (old_col_name, new_col)
) x
pivot
(
count(new_col)
for new_col in ([1], [2], [3],' + #cols + ')
) p '+ #orderby
exec sp_executesql #query;
See SQL Fiddle with Demo. This version will give a result:
| NAME | LOST | DRAW | WON | 2014 | 2013 | 2012 | TOTAL |
|----------|------|------|-----|------|------|------|-------|
| Person B | 4 | 0 | 2 | 6 | 2 | 2 | 10 |
| Person A | 2 | 1 | 1 | 4 | 3 | 4 | 11 |
| Person C | 1 | 1 | 1 | 3 | 1 | 0 | 4 |

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

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)

Pivot table to turn rows into columns

I currently run the query
SELECT [PriceAttributeID]
,[PriceID]
,[AttributeID]
,[PriceAttributeComparator]
,[PriceAttributeMin]
,[PriceAttributeMax]
FROM [PriceAttribute]
Which gives the output
1 2 1 1 S NULL
2 3 1 1 M NULL
3 4 1 1 L NULL
4 5 1 1 L NULL
5 5 2 1 Black NULL
I would like to get the output (where _Comp, _Min and _Max relate to PriceAttributeComparator, PriceAttributeMin and PriceAttributeMax)
PriceID 1_Comp 1_Min 1_Max 2_Comp 2_Min 2_Max
2 1 S NULL NULL NULL NULL
3 1 M NULL NULL NULL NULL
4 1 L NULL NULL NULL NULL
5 1 L NULL 1 Black NULL
The same query would also be expected to have 1_ and 2_ prefixes as 4_, 5_, 19_ and 32_ or any other indeterminate number of ID's based on what is in the table at the time.
I have attempted a PIVOT table, but i am new to them and haven't the first clue on how to create what it is i am looking to do.
Part of the problem you are probably having with the PIVOT function is due to the fact you have multiple columns that you want to apply the function to. If you want to use the PIVOT function, then I would suggest first unpivoting the columns PriceAttributeComparator, PriceAttributeMin and PriceAttributeMax. When you unpivot the data you will no longer have multiple columns, you will have multiple rows, then you can apply the pivot to all of the appropriate values.
You did not specify what version of SQL Server you are using but you can use CROSS APPLY with a UNION ALL to unpivot the columns:
select priceid,
col = cast(attributeid as varchar(10))+'_'+ col,
value
from
(
select PriceID,
AttributeID,
comp = cast(PriceAttributeComparator as varchar(10)),
[min] = cast(PriceAttributeMin as varchar(10)),
[max] = cast(PriceAttributeMax as varchar(10))
from PriceAttribute
) d
cross apply
(
select 'comp', comp union all
select 'min', [min] union all
select 'max', [max]
) c (col, value)
See Demo. This process will convert your data into the following format:
| PRICEID | COL | VALUE |
-----------------------------
| 2 | 1_comp | 1 |
| 2 | 1_min | S |
| 2 | 1_max | (null) |
| 3 | 1_comp | 1 |
| 3 | 1_min | M |
| 3 | 1_max | (null) |
Once the data is in multiple rows, then you can apply the PIVOT function to the values in col:
select priceid,
[1_comp], [1_min], [1_max], [2_comp], [2_min], [2_max]
from
(
select priceid,
col = cast(attributeid as varchar(10))+'_'+ col,
value
from
(
select PriceID,
AttributeID,
comp = cast(PriceAttributeComparator as varchar(10)),
[min] = cast(PriceAttributeMin as varchar(10)),
[max] = cast(PriceAttributeMax as varchar(10))
from PriceAttribute
) d
cross apply
(
select 'comp', comp union all
select 'min', [min] union all
select 'max', [max]
) c (col, value)
) src
pivot
(
max(value)
for col in ([1_comp], [1_min], [1_max], [2_comp], [2_min], [2_max])
) piv;
See SQL Fiddle with Demo.
The above versions work great if you have a known number of values but if the values are unknown, then you will need to use dynamic SQL to get the result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(cast(attributeid as varchar(10))+'_'+ col)
from
(
select distinct attributeid
from priceattribute
) d
cross apply
(
select 'comp', 1 union all
select 'min', 2 union all
select 'max', 3
) c (col, so)
group by attributeid, col, so
order by attributeid, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT priceid, ' + #cols + '
from
(
select priceid,
col = cast(attributeid as varchar(10))+''_''+ col,
value
from
(
select PriceID,
AttributeID,
comp = cast(PriceAttributeComparator as varchar(10)),
[min] = cast(PriceAttributeMin as varchar(10)),
[max] = cast(PriceAttributeMax as varchar(10))
from PriceAttribute
) d
cross apply
(
select ''comp'', comp union all
select ''min'', [min] union all
select ''max'', [max]
) c (col, value)
) x
pivot
(
max(value)
for col in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo. These solutions will give a result:
| PRICEID | 1_COMP | 1_MIN | 1_MAX | 2_COMP | 2_MIN | 2_MAX |
----------------------------------------------------------------
| 2 | 1 | S | (null) | (null) | (null) | (null) |
| 3 | 1 | M | (null) | (null) | (null) | (null) |
| 4 | 1 | L | (null) | (null) | (null) | (null) |
| 5 | 1 | L | (null) | 1 | Black | (null) |
It might be simplest to do this using conditional aggregation rather than pivot:
SELECT PriceID,
max(case when AttributeID = 1 then PriceAttributeComparator end) as comp_1,
max(case when AttributeID = 1 then PriceAttributeMin end) as min_1,
max(case when AttributeID = 1 then PriceAttributeMax end) as max_1,
max(case when AttributeID = 2 then PriceAttributeComparator end) as comp_2,
max(case when AttributeID = 2 then PriceAttributeMin end) as min_2,
max(case when AttributeID = 2 then PriceAttributeMax end) as max_2
FROM PriceAttribute pa
group by PriceId;

How can I return several text fields with the same ID number in one row in SQL server?

I have table:
ID Note
1 1 aaa
2 1 bbb
3 1 ccc
4 2 ddd
5 2 eee
6 2 fff
I need to return it as:
ID Note1 Note2 Note3
1 1 aaa bbb ccc
2 2 ddd eee fff
Thank you!
You can use the PIVOT function for this type of query. If you have a known number of columns, then you can hard-code the values:
select *
from
(
select id, note,
'Note' +
cast(row_number() over(partition by id order by id) as varchar(10)) col
from yourtable
) x
pivot
(
max(note)
for col in ([Note1], [Note2], [Note3])
) p
See SQL Fiddle with Demo
If you are going to have an unknown number of notes that you want to turn into columns, then you can use dynamic sql:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ','
+ QUOTENAME('Note' +
cast(row_number() over(partition by id order by id) as varchar(10)))
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT id,' + #cols + ' from
(
select id, note,
''Note'' +
cast(row_number() over(partition by id order by id) as varchar(10)) col
from yourtable
) x
pivot
(
max(note)
for col in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo
Both will produce the same results.
| ID | NOTE1 | NOTE2 | NOTE3 |
------------------------------
| 1 | aaa | bbb | ccc |
| 2 | ddd | eee | fff |
Or if you do not want to use the PIVOT function, then you can use an aggregate function with a CASE statement:
select id,
max(case when rn = 1 then note else '' end) Note1,
max(case when rn = 2 then note else '' end) Note2,
max(case when rn = 3 then note else '' end) Note3
from
(
select id, note,
row_number() over(partition by id order by id) rn
from yourtable
) src
group by id
See SQL Fiddle with Demo

Return Columns As Rows based on Number of Months

This seems like a simple thing to accomplish but I'm not sure if I am thinking about it correctly to get the desired results. I'm using a pivot but I think I need something else paired with it.
I have an invoice table that contains monthly invoices for each client. At most, a client will have 12 invoices per year, 1 for each month.
+----------+-------+-------+--------------+--------------+--------------+
| ClientID | Month | Year | ColumnValue1 | ColumnValue2 | ColumnValue3 |
+----------+-------+-------+--------------+--------------+--------------+
| 1 | 1 | 2012 | 20 | 30 | 50 |
| 1 | 2 | 2012 | 25 | 35 | 40 |
| 2 | 1 | 2012 | 28 | 38 | 48 |
+----------+-------+-------+--------------+--------------+--------------+
Now, I want to create a list like below based on each client. There would be a column for each month. So Client 1 would look like:
+--------------+----+----+---+---+---+---+---+---+---+----+----+----+-------+
| ColumnName | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Total |
+--------------+----+----+---+---+---+---+---+---+---+----+----+----+-------+
| ColumnValue1 | 20 | 25 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 45 |
| ColumnValue2 | 30 | 35 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 65 |
| ColumnValue3 | 50 | 40 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 90 |
+--------------+----+----+---+---+---+---+---+---+---+----+----+----+-------+
This can be done using both the UNPIVOT and PIVOT function in SQL Server. If you have a known number of columns, then you can use a static version:
select clientid,
col, year,
isnull([1], 0) [1],
isnull([2], 0) [2],
isnull([3], 0) [3],
isnull([4], 0) [4],
isnull([5], 0) [5],
isnull([6], 0) [6],
isnull([7], 0) [7],
isnull([8], 0) [8],
isnull([9], 0) [9],
isnull([10], 0) [10],
isnull([11], 0) [11],
isnull([12], 0) [12],
(isnull([1], 0) + isnull([2], 0) + isnull([3], 0)
+ isnull([4], 0) + isnull([5], 0) + isnull([6], 0)
+ isnull([7], 0) + isnull([8], 0) + isnull([9], 0)
+ isnull([10], 0) + isnull([11], 0) + isnull([12], 0) ) Total
from
(
select clientid, col, month, year, value
from yourtable
unpivot
(
value for col in (ColumnValue1, ColumnValue2, ColumnValue3)
) u
) x
pivot
(
sum(value)
for month in ([1], [2], [3], [4], [5], [6], [7],
[8], [9], [10], [11], [12])
) p
See SQL Fiddle with Demo
But it might be considerably easier to use dynamic sql to perform this operation, then there is less code to write and this will adjust the number of months based on what you have in your data sample:
DECLARE #colsUnpivot AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#colsPivot as NVARCHAR(MAX),
#colsTotal as NVARCHAR(MAX),
#colsNull as NVARCHAR(MAX)
select #colsUnpivot = stuff((select ','+ quotename(C.name)
from sys.columns as C
where C.object_id = object_id('yourtable') and
C.name like 'ColumnValue%'
for xml path('')), 1, 1, '')
select #colsPivot = STUFF((SELECT distinct ', ' + quotename(Month)
from yourtable t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #colsNull = STUFF((SELECT distinct ', IsNull('
+ quotename(Month) + ', 0) as '+quotename(Month)
from yourtable t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #colsTotal = STUFF((SELECT distinct '+ IsNull('
+ quotename(Month) + ', 0)'
from yourtable t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'select clientid,
year,
'+#colsNull+', '+#colsTotal+' as Total
from
(
select clientid, col, month, year, value
from yourtable
unpivot
(
value for col in ('+#colsUnpivot+')
) u
) x
pivot
(
sum(value)
for month in('+ #colspivot +')
) p'
exec(#query)
See SQL Fiddle with Demo
Both will produce the same results, the difference is that the second will adjust based on the data in your table:
| CLIENTID | YEAR | 1 | 2 | TOTAL |
-------------------------------------
| 1 | 2012 | 20 | 25 | 45 |
| 1 | 2012 | 30 | 35 | 65 |
| 1 | 2012 | 50 | 40 | 90 |
| 2 | 2012 | 28 | 0 | 28 |
| 2 | 2012 | 38 | 0 | 38 |
| 2 | 2012 | 48 | 0 | 48 |
Try this
Declare #t Table(ClientId Int,[Month] Int,[Year] Int,ColumnValue1 Int,ColumnValue2 Int, ColumnValue3 Int)
Insert Into #t Values(1,1,2012,20,30,50),(1,2,3012,25,35,40),(2,1,2012,28,38,48)
;With Cte As
(
Select ClientId,[Month],ColumnName,ColumnNameValues
From #t
UnPivot(ColumnNameValues For ColumnName In (ColumnValue1,ColumnValue2,ColumnValue3)) As unpvt
)
Select ClientId,
ColumnName
,[1] = Coalesce([1],0)
,[2] = Coalesce([2],0)
,[3] = Coalesce([3],0)
,[4] = Coalesce([4],0)
,[5] = Coalesce([5],0)
,[6] = Coalesce([6],0)
,[7] = Coalesce([7],0)
,[8] = Coalesce([8],0)
,[9] = Coalesce([9],0)
,[10]= Coalesce([10],0)
,[11]= Coalesce([11],0)
,[12] = Coalesce([12],0)
,Total = Coalesce([1],0) + Coalesce([2],0) + Coalesce([3],0) + Coalesce([4],0) +
Coalesce([5],0) + Coalesce([6],0) + Coalesce([7],0) + Coalesce([8],0) +
Coalesce([9],0) + Coalesce([10],0) + Coalesce([11],0) + Coalesce([12],0)
From Cte
PIVOT
(
MAX(ColumnNameValues) For [Month] In ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12] )
) As pvt
--Where ClientId =1 -- uncomment for specific client report
Order By 1
Result
Create table sequence (seqid bigint)
go
--Create a table which has sequence from 1 to 12 for monthId
Insert into sequence
Select Top 12 ROW_NUMBER() over(order by name)
from sys.objects
go
USE tempdb
GO
CREATE TABLE TestReport
(
ClientId int
,MonthId int
,YearId int
,val1 int
,val2 int
,val3 int
)
go
insert into TestReport
Select 1, 1,2012, 20,30,50
union
Select 1,2,2012,25, 35, 40
union
Select 2, 1, 2012, 28,38,48
Select *
from testReport
--Cross join with the Sequence table to get rows for each month
Select clientId
, seqid as monthId
, YearId
, case when MonthId = seqid then val1 else 0 end val1
, case when MonthId = seqid then val2 else 0 end val2
, case when MonthId = seqid then val3 else 0 end val3
into #Temp
from sequence seq
cross join testReport rpt
where seq.seqid <=12
--Select * from #Temp
SELECT 'ColumnValue1' AS [columnName], [1], [2], [3], [4],[5],[6],[7],[8],[9],[10],[11],[12]
,[1]+ [2]+ [3]+ [4]+ [5]+ [6]+ [7]+ [8]+ [9]+ [10]+ [11]+ [12] as Total
FROM
(SELECT monthId, val1
FROM #Temp
where ClientId =1
) AS SourceTable
PIVOT
(
max(val1) FOR MonthId IN ( [1], [2], [3], [4],[5],[6],[7],[8],[9],[10],[11],[12])
)
AS PivotTable
go
Drop table #Temp
Drop table sequence
drop table TestReport
go