show column value in 'previous row' and in a new column - sql

I have a table with four columns; Plant_Id, Year, Month and MR. I would like to be able to calculate the Range (month 2 month difference) of MR.
A sample of the table (MaintenanceRatebepaen is the table name) looks like this:
Plant_ID Year Month MR
CCAR 2009 1 0,706452
CCAR 2009 2 0,625899
CCAR 2009 3 0,636678
CCAR 2009 4 0,736544
CCAR 2009 5 0,552023
CCAR 2009 6 0,418338
CCAR 2009 7 0,502732
CCAR 2009 8 0,64526
CCAR 2009 9 0,743333
CCAR 2009 10 0,555556
CCAR 2009 11 0,297561
CCAR 2009 12 0,338608
CCAR 2010 1 0,380783
Etc.
I'm not sure this is of interest, but Plant_id can have 25 different values, years from 2008 - 2012, months 1-12, MR is a calculated value.
The Query looks like this:
SELECT Plant_Id, Jaar, Maand
, (SUM(Compl) + 0.000) / SUM(Total) AS MR
FROM (
SELECT Plant_Id, Jaar, Late, EarlyJobs, OnTimeJobs, Maand
, SUM(EarlyJobs + OnTimeJobs) AS Compl
, SUM(EarlyJobs) + SUM(Late) + SUM(OnTimeJobs) AS Total
FROM MaintenanceRatebepaen AS MaintenanceRatebepaen_1
GROUP BY Plant_Id, Jaar, Maand, Late, OnTimeJobs, EarlyJobs
) AS MaintenanceRatebepaen
WHERE (Jaar >= 2009) AND (Jaar <= 2011) AND (Plant_Id = 'CCAR')
GROUP BY Jaar, Plant_Id, Maand
ORDER BY Plant_Id, Jaar, Maand
I'm new to SQL, I managed to get the above from books and google searches. But I can't get the Range calculated, any help is greatly appreciated!

Did you just want month by month differences?
Data
declare #data table (PlantId nvarchar(5), [Year] nvarchar(4), [Month] int, MR decimal(10,10))
INSERT #data VALUES
('CCAR','2009','1','0.706452'),('CCAR','2009','2','0.625899'),('CCAR','2009','3','0.636678'),('CCAR','2009','4','0.736544'),('CCAR','2009','5','0.552023'),('CCAR','2009','6','0.418338'),('CCAR','2009','7','0.502732'),('CCAR','2009','8','0.64526'),('CCAR','2009','9','0.743333'),('CCAR','2009','10','0.555556'),('CCAR','2009','11','0.297561'),('CCAR','2009','12','0.338608'),('CCAR','2010','1','0.380783')
Query
;with cte as (
SELECT *, ROW_NUMBER() OVER (ORDER BY Year DESC, Month DESC) AS RowNumber FROM #data
)
select d1.PlantId
, d1.Year
, d1.Month
, d1.MR
, d1.MR-d2.MR AS [ChangeMRFromPreviousMonth]
from cte d1
LEFT OUTER JOIN cte d2
on d2.RowNumber = (d1.RowNumber+1)
order by d1.RowNumber DESC
Results:
PlantId Year Month MR ChangeMRFromPreviousMonth
------- ---- ----------- --------------------------------------- ---------------------------------------
CCAR 2009 1 0.7064520000 NULL
CCAR 2009 2 0.6258990000 -0.0805530000
CCAR 2009 3 0.6366780000 0.0107790000
CCAR 2009 4 0.7365440000 0.0998660000
CCAR 2009 5 0.5520230000 -0.1845210000
CCAR 2009 6 0.4183380000 -0.1336850000
CCAR 2009 7 0.5027320000 0.0843940000
CCAR 2009 8 0.6452600000 0.1425280000
CCAR 2009 9 0.7433330000 0.0980730000
CCAR 2009 10 0.5555560000 -0.1877770000
CCAR 2009 11 0.2975610000 -0.2579950000
CCAR 2009 12 0.3386080000 0.0410470000
CCAR 2010 1 0.3807830000 0.0421750000
Is that what you needed? Your query contains alot of extra columns, so I don't know if you want that including?
* Edit *
In response to your comments, you need to put your query inside the CTE, with the additional RowNumber column. d1 and d2 are just aliases of that CTE. I think this should do it:
;with cte as (
SELECT Plant_Id, Jaar, Maand
, (SUM(Compl) + 0.000) / SUM(Total) AS MR
, ROW_NUMBER() OVER (ORDER BY Jaar DESC, Maand DESC) AS RowNumber
FROM (
SELECT Plant_Id, Jaar, Late, EarlyJobs, OnTimeJobs, Maand
, SUM(EarlyJobs + OnTimeJobs) AS Compl
, SUM(EarlyJobs) + SUM(Late) + SUM(OnTimeJobs) AS Total
FROM MaintenanceRatebepaen AS MaintenanceRatebepaen_1
GROUP BY Plant_Id, Jaar, Maand, Late, OnTimeJobs, EarlyJobs
) AS MaintenanceRatebepaen
WHERE (Jaar >= 2009) AND (Jaar <= 2011) AND (Plant_Id = 'CCAR')
GROUP BY Jaar, Plant_Id, Maand
ORDER BY Plant_Id, Jaar, Maand
)
select d1.PlantId
, d1.Jaar
, d1.Maand
, d1.MR
, d1.MR-d2.MR AS [ChangeMRFromPreviousMonth]
from cte d1
LEFT OUTER JOIN cte d2
on d2.RowNumber = (d1.RowNumber+1)
order by d1.RowNumber DESC

Related

SQL get year-on-year quarter-to-date revenue

Having the table below:
Year Quarter Month Revenue
2005 Q1 1 13
2006 Q1 1 10
2006 Q1 2 15
2006 Q1 3 35
2006 Q2 4 11
2006 Q2 5 15
2006 Q2 6 9
2007 Q1 1 6
2007 Q1 2 14
2007 Q1 3 7
2007 Q2 4 20
2007 Q2 5 6
2007 Q2 6 6
I need a query to calculate the year-on-year comparison of quarter-to-date revenue as below:
Year Quarter Month CUrrentQTDRevenue PreviousQTDRevenue
2005 Q1 1 13
2006 Q1 1 10 13
2006 Q1 2 25 13
2006 Q1 3 60 13
2006 Q2 4 11
2006 Q2 5 26
2006 Q2 6 35
2007 Q1 1 6 10
2007 Q1 2 20 25
2007 Q1 3 27 60
2007 Q2 4 20 11
2007 Q2 5 26 26
2007 Q2 6 32 35
I've managed to get the current year quarter-to-date revenue
SELECT Year, Quarter, Month
, SUM(Revenue) OVER (PARTITION BY Year, Quarter ORDER BY Year, Quarter, Month)
AS CurrentYearQuarterToDateRevenue
FROM revenue
but how do I get to the second part? Note that I can't simply join quarters and months since, for example, 2005 has only one month for Q1, so Q1 for 2006 will have 13 for every month.
In the example the prior year revenue is inconsistently applied. If the YQM revenue were cumulative by Quarter in 2007 vs 2006 as well as 2006 vs 2005, then the value of 13 would carry forward into month 2 and 3 of Q1. Something like this
with yqm_ytd_cte(Year, Quarter, Month, YQM_YTD_Revenue) as (
select Year, Quarter, Month,
sum(Revenue) over (partition by Year, Quarter order by Year, Quarter, Month)
from revenue)
select yy.*, isnull(yy_lag.YQM_YTD_Revenue, 0) as Prior_Year_YQM_YTD_Revenue
from yqm_ytd_cte yy
left join yqm_ytd_cte yy_lag on yy.Year=yy_lag.Year-1
and yy.Quarter=yy_lag.Quarter
and yy.Month=yy_lag.Month;
I think I would expand the data out and use window functions:
with yyyymm as (
select t.year, m.month, m.qtr, t.revenue, t.quarter
from (select distinct year from t) y cross join
(values (1, 1), (2, 1), . . . (12, 4)) m(month, qtr) left join
t
on t.year = y.year and t.month = m.month
)
select ym.*
from (select ym.*, lag(currentQTD, 12) over (order by year, month) as prevQTD
from (select ym.*,
sum(revenue) over (partition by year, qtr order by month) as currentQTD
from yyyymm ym
) ym
) ym
where quarter is null;
You can also use apply:
select t.*,
sum(revenue) over (partition by year, quarter order by month) as currentQTD,
tt.prevQTD
from t outer apply
(select sum(revenue) as prevQTD
from t tt
where tt.year = t.year - 1 and
tt.quarter = t.quarter and
tt.month <= t.month
) tt;

Limiting rows in SQL doesn't work properly

I want to get the first 5 rows of every season in my select. I have 4 seasons: SUM, SPR, AUT, WIN.
So there should be 20 rows in total.
My select looks like this:
select *
from (
select year, season, ROUND(avg(temperature),1) as avgTemp
from temperature join month on temperature.MONTH = month.MONTH
group by (season, year)
order by season, avgTemp asc
) where rownum <= 5;
It works for just one season. The output is:
1993 AUT 8,7
2007 AUT 9,9
1996 AUT 10
1998 AUT 10
2008 AUT 10,5
But it should look like that:
1996 SPR 9.6
1991 SPR 10.3
2006 SPR 10.3
2004 SPR 10.6
1995 SPR 10.6
1996 SUM 18.9
1993 SUM 19.1
2007 SUM 19.5
1998 SUM 19.5
2000 SUM 19.6
1993 AUT 8.7
2007 AUT 9.9
1998 AUT 10.0
1996 AUT 10.0
2008 AUT 10.5
1996 WIN .3
1991 WIN 1.2
2003 WIN 1.6
2006 WIN 1.9
2005 WIN 2.0
Do you know how to improve the select or do you have any other suggestions? Thanks in advance!
You need to do it in three steps:
Group by season and year, calculating the average temperature
Assign a row number: it restart with each season and assigns in ascending order according to the average temperature
Select only the rows with a row number between 1 and 5
The SQL should look like this (untested):
select year, season, avg_temp
from (
select year, season, avg_temp,
row_number() over(partition by season order by avg_temp) rn
from (
select year, season, ROUND(avg(temperature),1) as avg_temp
from temperature
join month on temperature.MONTH = month.MONTH
group by season, year
)
)
where rn <= 5;
Update
For you special ordering by season, add this:
order by case season
when 'SPR' then 1
when 'SUM' then 2
when 'AUT' then 3
when 'WIN' then 4
end, avg_temp;
WITH cteAverageTempByYearBySeason AS (
SELECT
year
,season
,ROUND(AVG(temperature),1) as AvgTemp
FROM
Temperature t
INNER JOIN Month m
On t.MONTH = m.MONTH
GROUP BY
year
,season
)
, cteRowNumber AS (
SELECT
*
,ROW_NUMBER() OVER (PARTITION BY season ORDER BY AvgTemp ASC) as RowNumber
FROM
cteAverageTempByYearBySeason
)
SELECT *
FROM
cteRowNumber
WHERE
RowNumber <= 5
Here is an example. I broke out the derived tables into Common Table Expressions to make the logic more noticeable. You need to create a PARTITIONED ROW_NUMBER() not just use oracles special rownumber. The latter will only return the same as TOP/LIMIT 5 where as the former will allow you to identify 5 rows per season.
Edit added a neat trick for your order by so you don't have to write a case expression. This one utilizes your month number which I assume is what MONTH column is.
WITH cteAverageTempByYearBySeason AS (
SELECT
year
,season
,ROUND(AVG(temperature),1) as AvgTemp
,MAX(m.MONTH) as SeasonOrderBy
FROM
Temperature t
INNER JOIN Month m
On t.MONTH = m.MONTH
GROUP BY
year
,season
)
, cteRowNumber AS (
SELECT
*
,ROW_NUMBER() OVER (PARTITION BY season ORDER BY AvgTemp ASC) as RowNumber
FROM
cteAverageTempByYearBySeason
)
SELECT
year
,season
,AVG
FROM
cteRowNumber
WHERE
RowNumber <= 5
ORDER BY
SeasonOrderBy
,AvgTemp
,Year
You need to use row_number to get 5 rows for each grouping:
select
year,
season,
round(avg(temperature), 1) as avgTemp
from (
select *,
row_number() over(partition by season, year order by season, avgTemp) as rn
from temperature t
join month m
on m.MONTH = t.MONTH
) a
where
a.rn <= 1

Running Total in Oracle SQL - insert missing rows

Let's assume I have following set of data in Oracle SQL database:
Product Year Month Revenue
A 2016 1 7
A 2016 5 15
After creating running totals with following code
select Product, Year, Month, Revenue,
sum(Revenue) over (partition by Product, Year order by Month) Revenue_Running
from exemplary_table
I receive following result:
Product Year Month Revenue Revenue_Running
A 2016 1 7 7
A 2016 5 15 22
Is there any way that I can get this:
Product Year Month Revenue Revenue_Running
A 2016 1 7 7
A 2016 2 (null) 7
A 2016 2 (null) 7
A 2016 4 (null) 7
A 2016 5 15 22
You need a calendar table and Left join with your exemplary_table
SELECT p.product,
c.year,
c.month,
COALESCE(revenue, 0),
Sum(revenue)OVER (partition BY p.product, c.year ORDER BY c.month) Revenue_Running
FROM calendar_table c
CROSS JOIN (SELECT DISTINCT product
FROM exemplary_table) p
LEFT JOIN exemplary_table e
ON c.year = e.year
AND e.month = c.month
WHERE c.dates >= --your start date
AND c.dates <= --your end date

SQL Ranking Dates to Get Year Over Year Order

I have a list of dates in a YYYYMM format and I am trying to rank them in a Year over Year format that would look like the following:
MonthDisplay YearMonth Rank MonthNumber YearNumber
Aug-2013 201308 1 8 2013
Aug-2012 201208 2 8 2012
Jul-2013 201307 3 7 2013
Jul-2012 201207 4 7 2012
I have been able to get it close by using the following Rank and get the results below:
RANK() OVER(PARTITION BY 1 ORDER BY MonthNumber DESC, YearNumber DESC)
Month YearMonth Rank
Dec-2012 201212 1
Dec-2011 201112 2
Nov-2012 201211 114
Nov-2011 201111 115
Oct-2012 201210 227
Oct-2011 201110 228
However, this starts with Dec-2012 instead of the Aug-2013 (current month). I can't figure out how to get it to start with the current month. I am sure it something super easy and I am just missing it. Thanks!
select
T.YearMonth,
rank() over (order by R.rnk asc, D.YearNumber desc) as [Rank],
D.MonthNumber, D.YearNumber
from Table1 as T
outer apply (
select
month(getdate()) as CurMonthNumber,
cast(right(T.YearMonth, 2) as int) as MonthNumber,
cast(left(T.YearMonth, 4) as int) as YearNumber
) as D
outer apply (
select
case
when D.MonthNumber <= D.CurMonthNumber then
D.CurMonthNumber - D.MonthNumber
else
12 + D.CurMonthNumber - D.MonthNumber
end as rnk
) as R
sql fiddle example

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;