SQL Afficianados,
There has got to be a better way than the road I am going down...
SQL Fiddle HERE
Using SQL Server 2008. In short, I have a table with months as columns. i.e.:
CREATE TABLE MyData (MyID VARCHAR(10),
JAN MONEY, FEB MONEY, MAR MONEY, APR MONEY, MAY MONEY, JUN MONEY,
JUL MONEY, AUG MONEY, SEP MONEY, OCT MONEY, NOV MONEY, DEC MONEY);
Given a month value of N (as integer, where 1 = JAN and 12 = DEC), I want to calculate a "To Date" value from the first month to the Nth month.
So, given this simple data:
INSERT INTO MyData (MyID, JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC)
SELECT 'Rec1', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
UNION ALL SELECT 'Rec2', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
UNION ALL SELECT 'Rec3', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
;
I want to pass in any month value and add up the proper month columns. Given the sample data above, here is a chart of expected results based on the value of the month passed in...
Month Value | Expected Result |
------------+------------------
1 | 3
2 | 9
3 | 18
4 | 30
5 | 45
6 | 63
7 | 84
8 | 108
9 | 135
10 | 165
11 | 198
12 | 234
I know I could do this with a CASE statement like this:
DECLARE #v_Month INT = 2
SELECT CASE
WHEN #v_Month = 1 THEN SUM(JAN)
WHEN #v_Month = 2 THEN SUM(JAN) + SUM(FEB)
WHEN #v_Month = 2 THEN SUM(JAN) + SUM(FEB) + SUM(MAR)
--You get the idea. The pattern would continue for the rest of the months.
ELSE 0
END AS ToDateSum
FROM MyData
But is there a better way? Teach me, oh great ones of SQL Server.
You can use unpivot to change the data to a more amenable shape. It might make more sense to store it this way in the first place.
declare #v_Month int = 2;
with u as (
select
myid, [month], amount
from (
select
myid, jan [1], feb [2], mar [3], apr [4], may [5], jun [6],
jul [7], aug [8], sep [9], oct [10], nov [11], dec [12]
from
MyData
) p
unpivot (
amount for [month] in ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])
) u
) select
sum(amount)
from
u
where
[month] <= #v_Month;
Example SQLFiddle
You can simplify this a bit, if you want to keep the data in the same structure:
select
sum(amount)
from (
select
myid, jan [1], feb [2], mar [3], apr [4], may [5], jun [6],
jul [7], aug [8], sep [9], oct [10], nov [11], dec [12]
from
MyData
) p
unpivot (
amount for [month] in ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])
) u
where
[month] <= #v_Month;
Something like this will work:
declare #MyDesiredMonth int = 3
with normalized_view as
(
select MyId , Month = 1 , Value = Jan from MyData
UNION ALL select MyId , Month = 2 , Value = Feb from MyData
UNION ALL ...
UNION ALL select MyId , Month = 11 , Value = Nov from MyData
UNION ALL select MyId , Month = 12 , Value = Dec from MyData
)
select MonthlyTotal = sum(Value)
from normalized_view t
where t.Month = #myDesiredMonth
Alternatively, something like this:
declare #MyDesiredMonth int = 3
select MonthlyTotal = sum( case #MyDesiredMonth
when 1 then t.Jan
when 2 then t.Feb
...
when 11 then t.Nov
when 12 then t.Dec
end
)
from MyTable t
Either one should work with the execution plan likely winding up similar.
Related
I have a SQL query than transpose rows by columns but i dont know how to rename the columns.
select *
from
(select CustomerID, FiscalPeriod, SaleAmtLocalCurr
from PerfTrk.dbo.IRIS_SaleFact
where CountryCode = '00001'
and DivisionCode = 'INS'
and SaleAmtLocalCurr > 0
and datefromparts(FiscalYear, FiscalPeriod, 1) between datefromparts(2019,9, 1) and datefromparts(2020,8, 31)) d
pivot
(sum(SaleAmtLocalCurr) for FiscalPeriod in ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])
) piv;
The result is
CustomerID 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
I want to rename the columns with the names of months
Thanks
CASE is always an option
select
*
from
(select
CustomerID,
(CASE
WHEN FiscalPeriod = 1 THEN 'JAN'
WHEN FiscalPeriod = 2 THEN 'FEB'
WHEN FiscalPeriod = 3 THEN 'MAR'
WHEN FiscalPeriod = 4 THEN 'APR'
WHEN FiscalPeriod = 5 THEN 'MAY'
WHEN FiscalPeriod = 6 THEN 'JUN'
WHEN FiscalPeriod = 7 THEN 'JUL'
WHEN FiscalPeriod = 8 THEN 'AUG'
WHEN FiscalPeriod = 9 THEN 'SEP'
WHEN FiscalPeriod = 10 THEN 'OCT'
WHEN FiscalPeriod = 11 THEN 'NOV'
WHEN FiscalPeriod = 12 THEN 'DEC'
END) AS 'Month',
SaleAmtLocalCurr
from
PerfTrk.dbo.IRIS_SaleFact
where
CountryCode = '00001'
and DivisionCode = 'INS'
and SaleAmtLocalCurr > 0
and datefromparts(FiscalYear, FiscalPeriod, 1)
between datefromparts(2019,9, 1) and datefromparts(2020,8, 31)) d
pivot
(sum(SaleAmtLocalCurr) for FiscalPeriod in ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])
) piv;
I have a schema like this
demo(month_year(navarchar), datecount(int), destination(nvarchar), type(nvarchar)).
In output i want to transform the rows to columns with concatenate columns.
datecount | Month_year | destination type
-------------------------+---------------+-------------------
07 | March - 18 | ABC No
23 | August - 2018 | ABC No
29 | August - 2018 | XYZ Sold Out
04 | July - 2018 | PQR Sold Out
10 | July - 2018 | XYZ No
25 | July - 2018 | ABC Sold Out
In the output i want,
Month_Year 1 2 3 ...
July - 2018 04(Sold Out-PQR) 10(No-XYZ) 25(Sold Out-ABC)
August - 2018 23(No-ABC) 29(Sold Out-XYZ)
I have tried more using the PIVOTE function. Facing the problem to display combination of datecount with destination and type as per above expected output. But could not get the solution. Please help me.
Just concatenate the values before pivoting.
;WITH ToPivot AS
(
SELECT
D.month_year,
ConcatenatedValues = CONVERT(VARCHAR(10), D.datecount) + '(' + D.type + '-' + D.destination + ')',
NumberToPivot = ROW_NUMBER() OVER (PARTITION BY D.month_year ORDER BY D.datecount ASC)
FROM
Demo AS D
)
SELECT
P.*
FROM
ToPivot AS T
PIVOT (
MAX(T.ConcatenatedValues)
FOR T.NumberToPivot IN (
[1], [2], [3], [4], [5], [6], [7], [8], [9], [10],
[11], [12], [13], [14], [15], [16], [17], [18], [19],
[20], [21], [22], [23], [24], [25], [26], [27], [28],
[29], [30], [31])
) AS P
I have a table in SQL Server 2014 with this schema:
OccuredDate (date) TypeID (int)
2014-1-1 1
2014-1-2 1
2014-2-5 4
2015-5-23 2
2015-6-3 3
…it has thousands of rows comprised of dates & typeIDs, spanning years.
So that I can plot this to a charting component, I’m trying to build a query that for a given year 1) returns one row per-month that 2) counts the total number of TypeID instances for the given TypeIDs. The charting component prefers columns for the type counts.
So for "2014" it would look like this:
MonthDate TypeOne TypeTwo TypeThree TypeFour
2014-1-1 2 0 0 0
2014-2-1 0 0 0 1
or:
Year Month TypeOne TypeTwo TypeThree TypeFour
2014 Jan 2 0 0 0
2014 Feb 0 0 0 1
Spent most of the night on it but no luck. Is there some dark SQL magic that will do this?
thanks!
You can do it using pivot, with something like this:
SELECT OccuredDate, [1], [2], [3], [4]
FROM
(
SELECT OccuredDate, TypeID FROM Table1) AS SourceTable
PIVOT
(
count(TypeID) FOR TypeID IN ([1], [2], [3], [4])
) AS PivotTable
And per month version:
SELECT
DATEADD(month, DATEDIFF(month, 0, OccuredDate), 0) as Month,
sum([1]) as [1],
sum([2]) as [2],
sum([3]) as [3],
sum([4]) as [4]
FROM
(
SELECT OccuredDate, TypeID FROM Table1) AS SourceTable
PIVOT
(
count(TypeID) FOR TypeID IN ([1], [2], [3], [4])
) AS PivotTable
group by
DATEADD(month, DATEDIFF(month, 0, OccuredDate), 0)
You can test this in SQL Fiddle: daily and monthly
Edit: Rewrote the monthly SQL
I always get null value issues whenever I try to use a case statement on a data from the same column.
Data in database:
Month Sum
Jan 1000
Feb 2000
Mar 3000
Desired Result:
Jan Feb Mar
1000 2000 3000
When I tried using case statement I was running into null issue and was getting results like below:
Jan Feb Mar
1000 Null Null
Null 2000 Null
Null Null 3000
Here is the code that creates null value issue.
select AccountID,
sum(Case when DATEPART(month,EndDateTime) = 10 then Budget End) period1,
sum(case when DATEPART(month,EndDateTime) = 11 then Budget end) period2,
sum(case when DATEPART(month,EndDateTime) = 12 then Budget End) period3,
Description,
from Budget
where DATEPART(month,EndDateTime) in ('10','11','12')
group by AccountID,Description,EndDateTime
order by AccountID,Description,EndDateTime;
Using Pivot function I was able to generate the desired result.
SELECT
AccountID,
[1] AS Jan,
[2] AS Feb,
[3] AS Mar,
[4] AS Apr,
[5] AS May,
[6] AS Jun,
[7] AS Jul,
[8] AS Aug,
[9] AS Sep,
[10] AS Oct,
[11] AS Nov,
[12] AS Dec
FROM
(Select
AccountID,
Budget,
MONTH(EndDateTime) as TMonth
from
dbo.Budget) source
PIVOT
(
SUM(Budget)
FOR TMonth
IN ( [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12] )
) AS pvtMonth
The current issue I am now having is that I need to add one more sum function Sum(BudgetNet). Any suggestions?
Thanks.
For the type of result that you are wanting, it looks like you should be using PIVOT instead of CASE.
Let's consider such query:
SELECT *
FROM (
SELECT
YEAR(OrderDate) [Year],
MONTH(OrderDate) [Month],
SubTotal
FROM Sales.SalesOrderHeader
) TableDate
PIVOT (
SUM(SubTotal)
FOR [Month] IN (
[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12]
)
) PivotTable
It returns a table looking like this:
year 1 2 3 4 5 6 ... 12 //MONTH
2001 100 100 100 100 100 100 100
How can we group the columns and return a result in the form,for example:
year 1-3 4-5 5-10 11-12 //Group the columns!
2001 300 200 500 200
I need a T-SQL query to do this. I 've checked dynamic query execution but it also doesn't help much..
In the subquery, first put your months in the right buckets, then perform a pivot on that table.
SELECT *
FROM (
SELECT
YEAR(OrderDate) [Year],
CASE WHEN MONTH(OrderDate)<=3 THEN '1-3'
WHEN MONTH(OrderDate)<=5 THEN '4-5',
WHEN MONTH(OrderDate)<=10 THEN '6-10',
ELSE '11-12' END [MonthRange]
SubTotal
FROM Sales.SalesOrderHeader
) TableDate
PIVOT (
SUM(SubTotal)
FOR [MonthRange] IN (
['1-3'],['4-5'],['6-10'],['11-12']
)
) PivotTable
I don't use PIVOT that much (and don't have enough info here to re-create your sample), but can't you just say instead of SELECT *:
SELECT [year], [1-3] = [1] + [2] + [3], [4-5] = [4] + [5], ...
FROM ( SELECT YEAR ...) TableDate
PIVOT ( SUM(SubTotal) FOR ...) PivotTable;
EDIT
Adding a simple working example for #t-clausen.dk:
CREATE TABLE #x([year] INT, [month] INT, total INT);
INSERT #x SELECT 2011, 1, 15
UNION ALL SELECT 2011, 2, 20
UNION ALL SELECT 2011, 2, 30
UNION ALL SELECT 2011, 3, 15
UNION ALL SELECT 2011, 4, 22
UNION ALL SELECT 2011, 5, 13
UNION ALL SELECT 2011, 6, 22
UNION ALL SELECT 2011, 7, 12
UNION ALL SELECT 2011, 8, 14
UNION ALL SELECT 2011, 9, 30
UNION ALL SELECT 2011, 10, 30
UNION ALL SELECT 2011, 11, 20
UNION ALL SELECT 2011, 12, 45;
SELECT
[year],
[1-3] = [1] + [2] + [3],
[4-5] = [4] + [5],
[6-10] = [6] + [7] + [8] + [9] + [10],
[11-12] = [11] + [12]
FROM ( SELECT [year], [month], total FROM #x ) AS TableDate
PIVOT (
SUM(total)
FOR [month] IN
(
[1], [2], [3], [4],
[5], [6], [7], [8],
[9],[10],[11],[12]
)
) AS PivotTable
ORDER BY [year];
DROP TABLE #x;