table.
rows of my table. for january
rows for february
My sample query
select id2,
(select budget from tblmonth where id2='1' and month='January' and groups='CCARE') as jbud1,
(select actual from tblmonth where id2='1' and month='January' and groups='CCARE') as jact1,
(select variance from tblmonth where id2='1' and month='January' and groups='CCARE') as jvar1,
(select [percent] from tblmonth where id2='1' and month='January' and groups='CCARE') as jper1,
(select budget from tblmonth where id2='2' and month='February' and groups='CCARE') as fbud2,
(select actual from tblmonth where id2='2' and month='February' and groups='CCARE') as fact2,
(select variance from tblmonth where id2='2' and month='February' and groups='CCARE') as fvar2,
(select [percent] from tblmonth where id2='2' and month='February' and groups='CCARE') as fper2
from tblmonth where groups='CCARE' and id2='1' and month='January'
my problem with this query is it returns duplicate values.
i want to achieve is.
the columns budget, actual, variance, percent from each month will be this display in a single result. like this in the image.
thanks in advance :) im only new to sql and im using a sql server 2014.
At first I create temp table, similar to yours:
CREATE TABLE #tblmonth (
id2 smallint,
[year] varchar(50),
groups varchar(100),
element varchar(100),
[month] varchar(50),
budget decimal(18,2),
actual decimal(18,2),
variance decimal(18,2),
[percent] decimal(18,2),
)
INSERT INTO #tblmonth VALUES
(1, 2016, 'CCARE', 'Basic', 'January', 52.28, 43.00, 43.98, 0.00),
(2, 2016, 'CCARE', 'Bonuses', 'January', 1.77, 17.10, -46.12, 0.00),
(3, 2016, 'CCARE', 'Overtime', 'January', 2.34, 20.20, 7.98, 0.00),
(4, 2016, 'CCARE', 'Comminication', 'January', 19.01, 27.34, -81.98, 0.00),
(5, 2016, 'CCARE', 'HDMF', 'January', 0.98, 22.17, -22.98, 0.00),
(1, 2016, 'CCARE', 'Basic', 'February', 152.28, 3.00, 4.98, 0.00),
(2, 2016, 'CCARE', 'Bonuses', 'February', 12.77, 1.10, -4.12, 0.00),
(3, 2016, 'CCARE', 'Overtime', 'February', 23.34, 3.20, 0.98, 0.00),
(4, 2016, 'CCARE', 'Comminication', 'February', 191.01, 2.34, -1.98, 0.00),
(5, 2016, 'CCARE', 'HDMF', 'February', 10.98, 2.17, -2.98, 0.00)
Then I ran dynamic SQL + UNPIVOT + PIVOT (about pivoting you can read on MSDN and here):
DECLARE #sql nvarchar(max),
#columns nvarchar(max)
--here we get column names for pivoting, so we dont need to write them by hand
SELECT #columns = STUFF((
SELECT ',budget'+[MONTH] + ',actual'+[MONTH]+ ',variance'+[MONTH]+',percent'+[MONTH]
FROM #tblmonth
GROUP BY [MONTH]
FOR XML PATH('')),1,1,'')
-- Main query, at first we UNPIVOT so we make column to rows,
-- then we pivot them back in a way we need.
SELECT #sql = '
SELECT *
FROM (
SELECT [year],
groups,
element,
[rows] + [month] as [rowname],
[values]
FROM (
SELECT id2,
[year],
groups,
element,
[month],
CAST(budget as varchar(50)) as budget,
CAST(actual as varchar(50)) as actual,
CAST(variance as varchar(50)) as variance,
CAST([percent] as varchar(50)) as [percent]
FROM #tblmonth) as p
UNPIVOT (
[values] FOR [rows]
IN (budget, actual, variance, [percent])
) as unpvt
) as p
PIVOT(
MAX([values]) FOR [rowname] IN ('+#columns+'
)
) as pvt'
EXEC sp_executesql #sql
Output:
year groups element budgetFebruary actualFebruary varianceFebruary percentFebruary budgetJanuary actualJanuary varianceJanuary percentJanuary
2016 CCARE Basic 152.28 3.00 4.98 0.00 52.28 43.00 43.98 0.00
2016 CCARE Bonuses 12.77 1.10 -4.12 0.00 1.77 17.10 -46.12 .00
2016 CCARE Comminication 191.01 2.34 -1.98 0.00 19.01 27.34 -81.98 0.00
2016 CCARE HDMF 10.98 2.17 -2.98 0.00 0.98 22.17 -22.98 0.00
2016 CCARE Overtime 23.34 3.20 0.98 0.00 2.34 20.20 7.98 0.00
Try this:
select t1.id2, t1.budget,t1.actual,t1.variance,t1[percent]
,t2.budget,t2.actual,t2.variance,t2.[percent]
from tblmonth t1 inner join
(select * from tblmonth where groups='CCARE' and month='February' ) t2
on t1.id2 = t2.id2 and t1.year = t2.year
where t1.groups='CCARE' and t1.month='January'
It should be something like this :
select
q.id2, q.year, q.groups, q.element,
jan.month as jMonth, jan.budget as jBudget, jan.actual as jActual, jan.variance as jVariance, jan.[percent] as jPercent, jan.date_update as jDateUpdate,
feb.month as fMonth, feb.budget as fBudget, feb.actual as fActual, feb.variance as fVariance, feb.[percent] as fPercent, feb.date_update as fDateUpdate
from (select groups, year, id2, element from tblmonth
where groups='CCARE' and year=2016 group by groups, year, id2, element) q
left join (select * from tblmonth
where groups='CCARE' and year=2016 and month='January') jan
on (q.groups = jan.groups and q.year = jan.year and
q.id2 = jan.id2 and q.element = jan.element)
left join (select * from tblmonth
where groups='CCARE' and year=2016 and month='February') feb
on (q.groups = feb.groups and q.year = feb.year and
q.id2 = feb.id2 and q.element = feb.element);
Simply joining subqueries on the common fields.
The first subquery q isn't really crucial.
Because you could use one of the months instead to join other months to it.
But the SQL looks better this way if one would add extra months.
Although it would make the SQL smaller. For example:
select
jan.id2, jan.year, jan.groups, jan.element,
jan.month as jMonth, jan.budget as jBudget, jan.actual as jActual, jan.variance as jVariance, jan.[percent] as jPercent, jan.date_update as jDateUpdate,
feb.month as fMonth, feb.budget as fBudget, feb.actual as fActual, feb.variance as fVariance, feb.[percent] as fPercent, feb.date_update as fDateUpdate
from tblmonth jan
left join tblmonth feb on (jan.groups = feb.groups and jan.year = feb.year and jan.id2 = feb.id2
and jan.element = feb.element and feb.month = 'February')
where jan.groups='CCARE' and jan.year=2016 and jan.month='January';
Related
Noob alert...
I have an example table as followed.
I am trying to create a column in SQL that shows the what percentage each customer had of size S per year.
So output should be something like:
(Correction: the customer C for 2019 Percentage should be 1)
Window functions will get you there.
DECLARE #TestData TABLE
(
[Customer] NVARCHAR(2)
, [CustomerYear] INT
, [CustomerCount] INT
, [CustomerSize] NVARCHAR(2)
);
INSERT INTO #TestData (
[Customer]
, [CustomerYear]
, [CustomerCount]
, [CustomerSize]
)
VALUES ( 'A', 2017, 1, 'S' )
, ( 'A', 2017, 1, 'S' )
, ( 'B', 2017, 1, 'S' )
, ( 'B', 2017, 1, 'S' )
, ( 'B', 2018, 1, 'S' )
, ( 'A', 2018, 1, 'S' )
, ( 'C', 2017, 1, 'S' )
, ( 'C', 2019, 1, 'S' );
SELECT DISTINCT [Customer]
, [CustomerYear]
, SUM([CustomerCount]) OVER ( PARTITION BY [Customer]
, [CustomerYear]
) AS [CustomerCount]
, SUM([CustomerCount]) OVER ( PARTITION BY [CustomerYear] ) AS [TotalCount]
, SUM([CustomerCount]) OVER ( PARTITION BY [Customer]
, [CustomerYear]
) * 1.0 / SUM([CustomerCount]) OVER ( PARTITION BY [CustomerYear] ) AS [CustomerPercentage]
FROM #TestData
ORDER BY [CustomerYear]
, [Customer];
Will give you
Customer CustomerYear CustomerCount TotalCount CustomerPercentage
-------- ------------ ------------- ----------- ---------------------------------------
A 2017 2 5 0.400000000000
B 2017 2 5 0.400000000000
C 2017 1 5 0.200000000000
A 2018 1 2 0.500000000000
B 2018 1 2 0.500000000000
C 2019 1 1 1.000000000000
Assuming there are no duplicate rows for a customer in a year, you can use window functions:
select t.*,
sum(count) over (partition by year) as year_cnt,
count * 1.0 / sum(count) over (partition by year) as ratio
from t;
Break it apart into tasks - that's probably the best rule to follow when it comes to SQL. So, I created a variable table #tmp which I populated with your sample data, and started out with this query:
select
customer,
year
from #tmp
where size = 'S'
group by customer, year
... this gets a row for each customer/year combo for 'S' entries.
Next, I want the total count for that customer/year combo:
select
customer,
year,
SUM(itemCount) as customerItemCount
from #tmp
where size = 'S'
group by customer, year
... now, how do we get the count for all customers for a specific year? We need a subquery - and we need that subquery to reference the year from the main query.
select
customer,
year,
SUM(itemCount) as customerItemCount,
(select SUM(itemCount) from #tmp t2 where year=t.year) as FullTotalForYear
from #tmp t
where size = 'S'
GROUP BY customer, year
... that make sense? That new line in the ()'s is a subquery - and it's hitting the table again - but this time, its just getting a SUM() over the particular year that matches the main table.
Finally, we just need to divide one of those columns by the other to get the actual percent (making sure not to make it int/int - which will always be an int), and we'll have our final answer:
select
customer,
year,
cast(SUM(itemCount) as float) /
(select SUM(itemCount) from #tmp t2 where year=t.year)
as PercentageOfYear
from #tmp t
where size = 'S'
GROUP BY customer, year
Make sense?
With a join of 2 groupings:
the 1st by size, year, customer and
the 2nd by size, year.
select
t.customer, t.year, t.count, t.size,
ty.total_count, 1.0 * t.count / ty.total_count percentage
from (
select t.customer, t.year, sum(t.count) count, t.size
from tablename t
group by t.size, t.year, t.customer
) t inner join (
select t.year, sum(t.count) total_count, t.size
from tablename t
group by t.size, t.year
) ty
on ty.size = t.size and ty.year = t.year
order by t.size, t.year, t.customer;
See the demo
I have a query that looks at profits and operations costs of different stores based on the fiscal year, and currently the fiscal years and variables are sorted into single, respective columns such as:
FiscalYear Metric Store Amount
2017 Profit A 220
2017 Cost A 180
2018 Profit B 200
2018 Cost B 300
...
I need to cross tab the rows so that for each store, I can compare the 2017 profit against the 2018 profit, and 2017 cost against the 2018 cost.
I broke out profits and costs by creating CASE WHEN statements for the ProfitLossTable, but I don't know how to make it create a "2017 Profit" and "2018 Profit" column, respectively, for each Store.
WITH [Profits, Cost] AS
(
SELECT ID, StoreID, Number, FYYearID,
CASE WHEN ID = 333 then Number END AS Profit
CASE WHEN ID = 555 then Number END AS Cost
FROM ProfitLossTable
),
Location AS
(
Select StoreID, StoreName
FROM StoreTable
),
FiscalMonth AS
(
SELECT FYYearID, FYYear
FROM FiscalMonthTable
)
SELECT A.Profit, A.Cost
FROM [Profits, Cost] A
JOIN Location B
ON A.StoreID = B.StoreID
JOIN FiscalMonth C
ON A.FYYearID = C.FYYearID
The code above shows this, and I feel like I am close to creating columns based on year, but I don't know what to do next.
FiscalYear Store Profit Cost
2017 A 220 100
2017 A 180 100
2018 B 200 100
2018 B 300 100
As a working (on my machine anyway ;-p) example using your data:
create table #temp(
FiscalYear int not null,
Metric nvarchar(50) not null,
Store nvarchar(10) not null,
Amount int not null
)
insert into #temp
values
(2017, N'Profit', N'A', 220),
(2017, N'Cost', N'A', 180),
(2018, N'Profit', N'B', 200),
(2018, N'Cost', N'B', 300)
select * from #temp
select Metric,
[2017] as [2017],
[2018] as [2018]
from (select FiscalYear, Amount, Metric from #temp) base_data
PIVOT
(SUM(Amount) FOR FiscalYear in ([2017], [2018])
) as pvt
order by pvt.Metric
drop table #temp
i'm trying to pivot dynamic pivot on multiple columns in SQL Server 2012
My table is as below
customer UnitNo invoiceNo invDate heads Amt periodFrmDT periodToDT
abc GF-3 C0000001 2015-11-01 Charge1 100 2015-11-01 2015-11-30
abc GF-3 C0000001 2015-11-01 Charge2 500 2015-11-10 2015-12-10
abc GF-3 C0000001 2015-11-01 charge3 600 2015-10-01 2015-10-30
and i want result like below
customer unitNo invoiceNo invDate Charge1 PeriodFrmDT periodToDT Charge2 PeriodFrmDT periodToDT Charge3 PeriodFrmDT periodToDT
abc GF-3 C0000001 2015-11-01 100 2015-11-01 2015-11-30 500 2015-11-10 2015-12-10 600 2015-10-01 2015-10-30
i have tried yet
select * from (
select c.Customer,unitNo, ih.invoiceNo
,bh.heads,it.Amt,periodFrmDT,PeriodToDT
from invHeader ih
inner join customer c on c.custID =ih.custID
inner join mstUnit mu on mu.unitID=ih.unitID
inner join invTran it on it.invID=ih.invHeaderID
inner join billingHeads bh on bh.BillHeadID=it.headID
)P
Pivot
(
max(amt)
for Head in([charge1],[charge2],[charge3])
) as pvt
Please help..
This will give you the table you have specified. The key thing here is that you must unpivot the amount, from date and to date for each charge first. This then allows you to repivot and see the values for all 3.
First I faked your data as follows:
declare #cte table (customer nvarchar(4)
, UnitNo nvarchar(4)
, invoiceNo nvarchar(8)
, invDate datetime
, heads nvarchar(8)
, Amt int
, periodFrmDT datetime
, periodToDT datetime)
insert into #cte ( customer, UnitNo, invoiceNo, invDate, heads, Amt, periodFrmDT, periodToDT)
values
('abc', 'GF-3', 'C0000001', '2015-11-01', 'Charge1', 100, '2015-11-01', '2015-11-30'),
('abc', 'GF-3', 'C0000001', '2015-11-01', 'Charge2', 500, '2015-11-10', '2015-12-10'),
('abc', 'GF-3', 'C0000001', '2015-11-01', 'charge3', 600, '2015-10-01', '2015-10-30');
Then I used the following query to get out the data as you requested:
with mycte as (select * from #cte
)
select * from
(
select customer, UnitNo ,invoiceNo ,invDate, heads + '_' + columnname as heads, value
from (select customer, UnitNo, invoiceNo, invDate, heads
, cast(Amt as nvarchar(10)) as Amt
, left(convert(nvarchar(10), periodFrmDT, 120), 10) as periodFrm
, left(convert(nvarchar(10), periodToDT, 120), 10) as periodTo
from mycte
) as t1
unpivot(value for columnname in (Amt, periodFrm ,periodTo)) as t2
) as t3 -- this table gives you your initial columns, then 2 more with the column headings for the next stage, and the values
-- columnname contains [Charge1_Amt], [Charge1_periodFrm], [Charge1_periodTo] etc
pivot (min(value) for heads in ( [Charge1_Amt], [Charge1_periodFrm], [Charge1_periodTo]
, [Charge2_Amt], [Charge2_periodFrm], [Charge2_periodTo]
, [charge3_Amt] , [charge3_periodFrm], [charge3_periodTo]
)
) as t4 -- And then we can re-pivot it all back up again.
To use the second query directly from you data, replace
with mycte as (select * from #cte
)
with
with mycte as (
-- This is the query with your base data
select c.Customer,unitNo, ih.invoiceNo, invDate ,bh.heads,it.Amt,periodFrmDT,PeriodToDT
from invHeader ih
inner join customer c on c.custID =ih.custID
inner join mstUnit mu on mu.unitID=ih.unitID
inner join invTran it on it.invID=ih.invHeaderID
inner join billingHeads bh on bh.BillHeadID=it.headID
)
For example table would be:
Customer OrderDate OrderAmt
-------- ---------- ---------
A1 20140101 920.00
A2 20140111 301.00
A2 20140123 530.00
A1 20140109 800.00
A3 20140110 500.00
A1 20140115 783.00
A3 20140217 500.00
A1 20140219 1650.00
A1 20140225 780.00
...
A3 20140901 637.00
I want to group them and calculate the sum(OrderAmt) within every 20 days and group by customer start from 20140101.
For what it's worth, you can accomplish what you describe with a pretty simple DATEDIFF() / GROUP BY operation, as below: whether or not that is actually what you want might be another question. I suspect that the DateBucket calculation might actually be something else ...
CREATE TABLE #tmpCustomer (Customer VARCHAR(2), OrderDate VARCHAR(10), OrderAmt DECIMAL(6,2))
INSERT INTO #tmpCustomer (Customer, OrderDate, OrderAmt)
SELECT 'A1',20140101, 920.00 UNION
SELECT 'A2',20140111, 301.00 UNION
SELECT 'A2',20140123, 530.00 UNION
SELECT 'A1',20140109, 800.00 UNION
SELECT 'A3',20140110, 500.00 UNION
SELECT 'A1',20140115, 783.00 UNION
SELECT 'A3',20140217, 500.00 UNION
SELECT 'A1',20140219, 1650.00 UNION
SELECT 'A1',20140225, 780.00 UNION
SELECT 'A3',20140901, 637.00
SELECT
Customer,
(DATEDIFF(day, '1/1/2014', CAST(OrderDate AS DATE)) / 20) + 1 AS DateBucket,
SUM(OrderAmt) SumOrderAmt
FROM #tmpCustomer
GROUP BY Customer, (DATEDIFF(day, '1/1/2014', CAST(OrderDate AS DATE)) / 20) + 1
ORDER BY Customer, DateBucket
You need to do two things:
(1) Create some sort of guide hold the '20 day groups' information. A Recursive CTE does this pretty well, and
(2) Recast that varchar date as an actual date for comparison purposes.
Then it's just joining the order data into that daterange grouping and summing the order amounts.
-------------------------
-- Here i'm just recreating your example
-------------------------
DECLARE #customerOrder TABLE (Customer varchar(2), OrderDate varchar(8), OrderAmt decimal(8,2))
INSERT INTO #customerOrder (Customer, OrderDate, OrderAmt)
VALUES
('A1', '20140101', 920.00),
('A2', '20140111', 301.00),
('A2', '20140123', 530.00),
('A1', '20140109', 800.00),
('A3', '20140110', 500.00),
('A1', '20140115', 783.00),
('A3', '20140217', 500.00),
('A1', '20140219', 1650.00),
('A1', '20140225', 780.00),
('A3', '20140901', 637.00)
-------------------------
-- Set up a table that lists off 20 day periods starting from 1/1/2014
-------------------------
DECLARE #startDate datetime, #endDate datetime;
SET #startDate = {d N'2014-01-01'};
SET #endDate = {d N'2014-12-31'};
WITH [dates] ([Sequence], [startDate], [maxExcludedDate]) AS
(SELECT 1 AS [Sequence]
,#startDate AS [startDate]
,DATEADD(d, 20, #startDate) AS [maxExcludedDate]
UNION ALL
SELECT Sequence + 1 AS Sequence
,DATEADD(d, 20, [startDate]) AS [startDate]
,DATEADD(d, 40, [startDate]) AS [maxExcludedDate]
FROM [dates]
WHERE [startDate] < #endDate
)
, dateFrame AS
(
SELECT
[startDate]
,[maxExcludedDate]
FROM [dates]
)
-------------------------
-- Set up a table that holds the orderDates as actual dates
-------------------------
, castData AS
(
SELECT
cast(orderdate as datetime) castDate
,OrderAmt
FROM #customerOrder
)
-------------------------
-- JOIN and sum.
-------------------------
SELECT
[startDate]
, Sum(OrderAmt) perodAmt
FROM
dateFrame df
left join castData cd
on cd.castDate >= df.startDate
and cd.castDate < df.maxExcludedDate
GROUP BY
[startDate]
ORDER by
[startDate]
Assuming that the OrderDate is a numeric field (not varchar). I'm also assuming that you don't need to go much more than a year in the future. If you want the gaps shown, keep the left join, if you don't want the gaps, then make it an inner join. (You can also make the hardcoded date a variable of where to start, I just kept it as the 20140101 that you mentioned.
with Numbers as
(Select 0 as Num
UNION ALL
Select Num+1
FROM Numbers
WHERE Num+1<= 20
)
, DateList AS
(Select Year(DateAdd(dd,20*(Num), Cast('2014-01-01' as date))) * 10000+Month(DateAdd(dd,20*(Num), Cast('2014-01-01' as date)))*100+Day(DateAdd(dd,20*(Num), Cast('2014-01-01' as date))) Groupingdatemin
, Year(DateAdd(dd,20*(Num+1)-1, Cast('2014-01-01' as date)))*10000+ MONTH(DateAdd(dd,20*(Num+1)-1, Cast('2014-01-01' as date)))*100+DAY(DateAdd(dd,20*(Num+1)-1, Cast('2014-01-01' as date))) Groupingdatemax
from Numbers
)
select Customer, sum(orderamt), Groupingdatemin, Groupingdatemax from DateLIst d LEFT JOIN
<yourtable> t on t.orderdate between d.Groupingdatemin and d.Groupingdatemax
group by customer, Groupingdatemin, Groupingdatemax
I have a query that returns one row. However, I want to invert the rows and columns, meaning show the rows as columns and columns as rows. I think the best way to do this is to use a pivot table, which I am no expert in.
Here is my simple query:
SELECT Period1, Period2, Period3
FROM GL.Actuals
WHERE Year = 2009 AND Account = '001-4000-50031'
Results (with headers):
Period1, Period2, Period3
612.58, 681.36, 676.42
I would like for the results to look like this:
Desired Results:
Period, Amount
Jan, 612.58
Feb, 681.36
Mar, 676.42
This is a simple example, but what I'm really after is a bit more comlex than this. I realize I could produce theses results by using several SELECT commands instead. I'm just hoping someone can shine some light on how to accomplish this with a Pivot Table or if there is yet a better way.
For a fixed set of fields, you can use a UNION of select statements, each statement fetching one of the fields. Standard SQL does not provide a way to transpose the result for a variable number of fields, e.g. select * from table.
If MSSQL has an extension which helps (like pivot), I don't know about it.
Try something like this (tested on MSSQL2008):
DECLARE #Data TABLE(Period1 Decimal(5, 2), Period2 Decimal(5, 2), Period3 Decimal(5, 2))
INSERT #Data
VALUES (612.58, 681.36, 676.42)
SELECT Period, Amount
FROM (SELECT Period1 AS Jan, Period2 AS Feb, Period3 AS Mar FROM #Data) AS D
UNPIVOT (Amount FOR Period IN (Jan, Feb, Mar)) AS U
Update
Based on Jeff's comment, how about this:
DECLARE #Actuals TABLE(Account INT, [Year] INT, Period1 Decimal(5, 2), Period2 Decimal(5, 2), Period3 Decimal(5, 2))
INSERT #Actuals VALUES (1, 2010, 612.58, 681.36, 676.42)
INSERT #Actuals VALUES (1, 2009, 512.58, 581.36, 576.42)
SELECT Account, Period, Amount
FROM
(
SELECT a.Account, a.Period1 AS Jan, a.Period2 AS Feb, a.Period3 AS Mar, a1.Period1 AS Jan1, a1.Period2 AS Feb1, a1.Period3 AS Mar1
FROM #Actuals AS a
LEFT OUTER JOIN #Actuals AS a1 ON a.Account = a1.Account AND a1.[Year] = a.[Year] - 1
WHERE a.[Year] = 2010
) AS d
UNPIVOT (Amount FOR Period IN (Jan, Feb, Mar, Jan1, Feb1, Mar1)) AS u
or, with an explicit year column:
DECLARE #Actuals TABLE(Account INT, [Year] INT, Period1 Decimal(5, 2), Period2 Decimal(5, 2), Period3 Decimal(5, 2))
INSERT #Actuals VALUES (1, 2010, 612.58, 681.36, 676.42)
INSERT #Actuals VALUES (1, 2009, 512.58, 581.36, 576.42)
SELECT Account, [Year], Period, Amount
FROM
(
SELECT a.Account, a.[Year], a.Period1 AS Jan, a.Period2 AS Feb, a.Period3 AS Mar
FROM #Actuals AS a WHERE a.[Year] IN (2009, 2010)
) AS d
UNPIVOT (Amount FOR Period IN (Jan, Feb, Mar)) AS u
Check the unnest function, its quite amazing with little overhead. Just add a select query on top of the nested select query and unnest at that point:
SELECT id,
unnest(array['a', 'b', 'c']) AS colname,
unnest(array[a, b, c]) AS thing
FROM foo
ORDER BY id;