Classifying months in periods - sql

Suppose I have 2 years of data. From January 2010 to Dec 2011.
I want to classify each of the months as periods. So January 2010 will be my 1, February 2010 my 2, and so on until December 2011 my 24 period.
I know I could do it something like:
select
year,mn,
case when year=2010 and mn=01 then 1
else when year=2010 and mn=02 then 2
else when year=2010 and mn=03 then 3
//and so on until // else when year=2011 and mn=12 then 24 end
from mytable;
The result would be something like:
year mn period
2010 1 1
2010 2 2
2010 3 3
2010 4 4
2010 5 5
2010 6 6
2010 7 7
2010 8 8
2010 9 9
2010 10 10
2010 11 11
2010 12 12
2011 1 13
2011 2 14
2011 3 15
2011 4 16
2011 5 17
2011 6 18
2011 7 19
2011 8 20
2011 9 21
2011 10 22
2011 11 23
2011 12 24
I want to avoid this kind of long and not wise method.

select
year, mn,
row_number() over (order by year, mn) as period
from t

No need for fancy windowing functions. Just do it the simple way. For a given {epoch-year} and {epoch-month} (e.g., 2010 and 1 respectively), the formula
( ( 12*year + mn ) - ( 12*{epoch-year} + {epoch-month} )
will give you the offset in month from the epoch. Add 1 to that and you have your period number. That leads you to something like this:
select year ,
mn ,
( ( 12*year + mn )
- ( 12*{epoch-year} + {epoch-month} )
) + 1 as period
...
from some-table
where year > {epoch-year}
OR ( year = {epoch-year} and mn >= {epoch-month} )
IF you don't have a specific epoch in mind, you can do something like this:
select t.year ,
t.mn ,
( ( 12*year + mn )
- ( 12*epoch.year + epoch.month )
) + 1 as period
...
from ( select year,mn
from some-table
order by year , mn
limit 1
) epoch
cross join some-table t
You should note that one can come up with a formula to number periods based on period lengths longer than 1 month: just compute the offset in months and and use integer division to divide that offset by the period length in months, thus getting to the sequential period number, something like
( ( 12*year + mn )
- ( 12*2010 + 1 )
) DIV 3 + 1 as period
should give you periods of 3 months in length

A cheap version for this particular case:
SELECT year, mn, (year - 2010) * 12 + mn AS period
FROM tbl;
This would also account for months that may be missing in your data.
And it would give you consistent numbers even when only selecting some rows.

Related

SQL - Splitting a row with week range into multiple rows

I have the following table structure and data in the database table:
ID
Year
StartWeek
EndWeek
AllocationPercent
5
2021
34
35
50
6
2021
1
3
5
I need to split the multi-week rows into multiple single-week rows, and the end result should be:
ID
Year
StartWeek
EndWeek
AllocationPercent
5
2021
34
34
50
5
2021
35
35
50
6
2021
1
1
5
6
2021
2
2
5
6
2021
3
3
5
Any help with this would be highly appreciated! There are a lot of threads regarding splitting date ranges into multiple rows but I cannot seem to modify those to fit my use case. I know that most likely I need a tally table with the week numbers (which I already have).
Another way to think about this is, because we know the max weeknumber is 53, to generate the set of all possible week numbers, then outer join to that set each week in any source row that is within that range.
;WITH n(n) AS
(
SELECT 0 UNION ALL SELECT n+1 FROM n WHERE n <= 53
)
SELECT w.ID,
w.Year,
StartWeek = n.n,
EndWeek = n.n,
w.AllocationPercent
FROM n
INNER JOIN dbo.TableName AS w
ON n.n BETWEEN w.StartWeek AND w.EndWeek
ORDER BY w.ID, w.Year, n.n;
Results:
ID
Year
StartWeek
EndWeek
AllocationPercent
5
2021
34
34
50
5
2021
35
35
50
6
2021
1
1
5
6
2021
2
2
5
6
2021
3
3
5
Example db<>fiddle
You can use recursive cte :
;with cte as (
select t.id, t.year, t.startweek, t.endweek, t.AllocationPercent
from t
union all
select id, year, startweek + 1, endweek, AllocationPercent
from cte c
where startweek < endweek
)
select id, year, startweek, startweek as endweek, AllocationPercent
from cte
order by id, startweek, endweek;
db fiddle

How to use 2 columns as "key" to get MAX value of selection (and on to next "key") in a SQL query

using a SQL query I am trying to get a max value from multiple rows, using 2 columns as 'key', and then sum them and move on t next 'key'
Here is an example table. It has years, userid and points. Each year has several weeks.
What I want to do is to take each users MAX points for each year and SUM them.
year
userid
week
points
2020
1
1
3
2020
1
3
3
2020
1
3
5
2020
1
4
12
2020
2
1
4
2020
2
2
4
2020
2
3
6
2020
2
4
10
2021
1
1
4
2021
1
2
5
2021
1
3
8
2021
1
4
9
2021
2
1
3
2021
2
2
6
2021
2
3
7
2021
2
4
13
I'd like the result for each year to be
User 1:
2020, 1, 12
2021, 1, 9
User 2:
2020, 2, 10
2021, 2, 13
...and after summing them, sorted by points:
userid
points
2
33
1
21
...and so forth (adding on users and years)
Any help is very much appreciated.
Per Gordon's helpful answer this is the query:
SELECT username, userdb.userid, SUM(points) as points FROM (SELECT standing.*, row_number() over (partition by standing.userid, year ORDER BY points desc) AS seqnum FROM standing) t JOIN userdb on userdb.userid = t.userid WHERE seqnum = 1 GROUP BY userid ORDER BY points DESC
You can use two levels of aggregation:
select userid, sum(max_points)
from (select userid, year, max(points) as max_points
from t
group by userid, year
) uy
group by userid;
Alternatively, you could handle this by filtering such as by using a window function:
select userid, sum(points)
from (select t.*,
row_number() over (partition by userid, year order by points desc) as seqnum
from t
) t
where seqnum = 1
group by userid;

Rollover sum with grouping in SQL Server with values repeating for empty fortnights

I need to do Rollover sum with grouping in SQL Server with values repeating for empty fortnights
I need to sum of sales within same year for the later fortnights.
Data is like shown below
Year fortnight sales
2011 1 10
2011 2 5
2011 5 30
2012 4 30
2012 5 2
I would need the output like below
Year | fortnight | salestillnowThisYear
2011 1 10
2011 2 15 (which is 10 + 5 from previous fortnight within same year)
2011 3 15 same as previous fortnight
2011 4 15 same as previous fortnight
2011 5 45 ( 10 + 5 + 30 from previous quaters of this year)
2011 6 45 same as previous fortnight
2011 7 45 same as previous fortnight
......and so on for each fortnight till last fortnight
2011 24 45 last fortnight data
2012 4 30 note this is the fortnight which first has value
2012 5 32 (which is 10 + 5 from previous fortnight within same year)
2012 6 32 last fortnight data
2012 7 32 last fortnight data
......and so on for each fortnight till last fortnight
2012 24 32 last fortnight data
Below is the available solution with me.
1 .Create temp table (rpt_fortnight) with 24 rows to represent 24 fortnights
join the data with that rpt_fortnight table to get data for all fortnights as (step2result)
SELECT YEar, fMaste.FortNightNumber as FortNightNumber,
sales as sales
FROM data AS FNI
inner join
rpt_fortNight as fMaste
on fMaste.FortnightNumber >= FNi.FortNightNumber --<--extrapolated data for next fortnights
3.combined data for fortnights from previous fortnights if available
SELECT year,
FortNightNumber,
Sum(sales) as sales
FROM step2result
group by year,FortNightNumber
we shall get data for all next fortnights with sum of previous fortnights in step 3
I wanted to check if there was any better solution
Try this. Use Recursive CTE to generate years with 1 to 24 fortnight.
CREATE TABLE #test
(
years INT,
fortnight INT,
sal INT
)
INSERT #test
VALUES (2011,1,10),
(2011,2,5),
(2011,5,30),
(2012,4,30),
(2012,5,2);
WITH cte
AS (SELECT DISTINCT 1 AS fortnight,
years
FROM #test
UNION ALL
SELECT fortnight + 1,
years
FROM cte
WHERE fortnight < 24),
CTE1
AS (SELECT a.years,
a.fortnight,
Isnull(sal, 0) SAL
FROM cte A
LEFT JOIN #test B
ON a.fortnight = b.fortnight
AND a.years = b.years)
SELECT *,
Sum(sal)
OVER(
partition BY years
ORDER BY fortnight rows UNBOUNDED PRECEDING) AS Running_Total
FROM CTE1 A
ORDER BY years,
fortnight

getting mutlitple row for a Oracle SQL query

I have a table as
userid cycleid ratings
1 13 5
1 14 6
1 15 7
I have to display data as
userid 2011 2012 2013
1 5 6 7
as you can see cycleid 13 is for year 2011, cycleid 14 for year 2012 and cycleid 15 for year 2013
MY QUERY
SELECT PER.USERID,
(SELECT max(PER1.RATING) FROM PERFRATINGS PER1 WHERE PER1.CYCLEID = 13) as 2011,
(SELECT max(PER2.RATING) FROM PERFRATINGS PER2 WHERE PER2.CYCLEID = 14) as 2012,
(SELECT max(PER3.RATING) FROM PERFRATINGS PER3 WHERE PER3.CYCLEID = 15) as 2013
FROM PERFRATINGS PER
Where PER.USERID = 1
gives multiple row (3 times)
userid 2011 2012 2013
1 5 6 7
1 5 6 7
1 5 6 7
I want everything in single row.
I suggest to use pivot
select * from
(select userid, cycleid, ratings from perfratings)
pivot
(max(ratings) for (cycleid) in (
12 as 2012,
13 as 2013,
14 as 2014
))
You could fix your query using group by or distinct. A more efficient version would use pivot (if available in your version of Oracle) or conditional aggregation:
SELECT PER.USERID,
max(case when PER1.CYCLEID = 13 then PER1.RATING end) as 2011,
max(case when PER1.CYCLEID = 14 then PER1.RATING end) as 2012,
max(case when PER1.CYCLEID = 15 then PER1.RATING end) as 2013
FROM PERFRATINGS PER
Where PER.USERID = 1
GROUP BY PER.USERID;
In a sense, the group by is redundant when you are looking only at one user. However, you can remove the where and see the results for all users.

How to get month and year in single column and grouping the data for all the years and months?

For the below query (sdate is column name and table name is storedata)
Collapse
WITH TotalMonths AS (SELECT T1.[Month], T2.[Year]
FROM ((SELECT DISTINCT Number AS [Month]
FROM MASTER.dbo.spt_values WHERE [Type] = 'p' AND Number BETWEEN 1 AND 12) T1 CROSS JOIN
(SELECT DISTINCT DATEPART(year, sdate) AS [Year]
FROM storedata) T2))
SELECT CTE.[Year], CTE.[Month], ISNULL(T3.[Sum], 0) areasum
FROM TotalMonths CTE LEFT OUTER JOIN (
SELECT SUM(areasft) [Sum], DATEPART(YEAR, sdate) [Year], DATEPART(MONTH, sdate) [Month]
FROM storedata
GROUP BY DATEPART(YEAR, sdate) ,DATEPART(MONTH, sdate)) T3
ON CTE.[Year] = T3.[Year] AND CTE.[Month] = T3.[Month] WHERE CTE.[Year]>'2007'
ORDER BY CTE.[Year], CTE.[Month]
I am getting result set like below.
YEAR MONTH AREASUM
2008 1 0
2008 2 1193
2008 3 4230
2008 4 350
2008 5 2200
2008 6 4660
2008 7 0
2008 8 6685
2008 9 0
2008 10 3051
2008 11 7795
2008 12 2940
2009 1 1650
2009 2 3235
2009 3 2850
2009 4 6894
2009 5 3800
2009 6 2250
2009 7 1000
2009 8 1800
2009 9 1550
2009 10 2350
2009 11 0
2009 12 1800
But I have to combine both month and year in single column. The reult set should like below.
JAN/08 O
FEB/08 1193
.. ..
.. ..
DEC/O9 1800
How can I modify my query? (I should display for all the years and months even if there is no area for a month)
Regards,
N.SRIRAM
Try:
SELECT CONVERT(VARCHAR(3), DATENAME(MONTH, CTE.Month), 7) + '/' + RIGHT(CTE.Year, 2)
instead of using your first 2 columns from your SELECT.
You seem to be saying that you're getting the right data from your original query, but the wrong format. So
Make a view out of the query you originally posted.
Build a SELECT query based on that view to give you the format you want.
Let's say you do this:
CREATE VIEW wibble AS <your original query goes here>
Then you can just query wibble to correct the formatting.
select
case
when month = 1 then 'Jan/'
when month = 2 then 'Feb/'
when month = 3 then 'Mar/'
when month = 4 then 'Apr/'
when month = 5 then 'May/'
when month = 6 then 'Jun/'
when month = 7 then 'Jul/'
when month = 8 then 'Aug/'
when month = 9 then 'Sep/'
when month = 10 then 'Oct/'
when month = 11 then 'Nov/'
when month = 12 then 'Dec/'
else 'Err'
end || substring(cast(year as CHAR(4)), 3, 2) as yearmonth,
areasum from wibble;