I've two columns containing sales from last year and this year and a data column with the number of weeks we are in. I want to calculate a yearly rolling sum in a new column from the week we are in back in the past till that same week.
In my example if i'm in week 2 this year my rolling sum will be sum all the values of sales last year from week 2 til week 52 plus sales from this year until week 2 including!!
Here's an example in excel of what my table and results would look like:
Assuming your data look like this
Table
+------+----------+-------+
| week | sales_ly | sales |
+------+----------+-------+
| 1 | 65 | 100 |
+------+----------+-------+
| 2 | 93 | 130 |
+------+----------+-------+
| 3 | 83 | 134 |
+------+----------+-------+
| 4 | 3083 | 59 |
+------+----------+-------+
| 5 | 30984 | 39 |
+------+----------+-------+
| 6 | 38 | 580 |
+------+----------+-------+
| 7 | 28 | 94 |
+------+----------+-------+
| 8 | 48 | 93 |
+------+----------+-------+
| 9 | 24 | 984 |
+------+----------+-------+
| 10 | 49 | 95 |
+------+----------+-------+
You need to create two cumulatives and sum them in the same measure.
Rolling Sum =
VAR CurrentYearCumulative =
CALCULATE(
SUM('Table'[sales]),
FILTER(ALLSELECTED('Table'),'Table'[week] <= MAX('Table'[week] ) )
)
VAR LastYearCumulative =
CALCULATE(
SUM('Table'[sales_ly]),
FILTER(ALLSELECTED('Table'),'Table'[week] >= MAX('Table'[week]) )
)
RETURN
CurrentYearCumulative + LastYearCumulative
The output
Related
I have a table and I want to sum rows that meet a certain criteria.
Table looks like this:
| Product | Sales_Num | Week | Cost | Retail |
|:------- |:---------:|:----:|:----:| ------:|
| PLA | 45281 | 38 | 53 | 88 |
| PLA2 | 45281 | 38 | 3 | 4 |
| CR25 | 45281 | 38 | 99 | 250 |
| BA34 | 45281 | 38 | 74 | 99 |
| PLA | 40251 | 38 | 53 | 75 |
| PLA2 | 40251 | 38 | 2 | 5 |
| CR25 | 40251 | 38 | 99 | 200 |
| BA34 | 40251 | 38 | 74 | 88 |
I want to Calculate the RETAIL column WHERE Product IN ('PLA','PLA2') AND Week = 38 and Sales_Num = 45281
Essentially, I want to Add 88 + 4 (first 2 rows above meet criteria). I want to eventually turn this into a function where I pass in Product, Week, and Sales_Num and I write the Calculation, Sales_Num, and Week to another table.
I was able to sum the rows I want, but I want the output to be [Sales_Num],[Week],[Total_Retail]
SELECT (
(SUM(CASE WHEN [Product]='PLA' AND [Sales_Num]=45281 AND [WEEK]=38 THEN [Retail] END) +
SUM(CASE WHEN [Product]='PLA2' AND [Sales_Num]=45281 AND [WEEK]=38 THEN [Retail] END)
)
) AS Total_Retail
select Sales_Num
,week
,sum(Retail) as Total_Retail
from t
where Week = 38
and Sales_Num = 45281
and Product in('PLA', 'PLA2')
group by Sales_Num, week
Sales_Num
week
Total_Retail
45281
38
92
Fiddle
SELECT Sales_Num, [Week], SUM(Retail) AS Total_Retail
FROM #yourtable
WHERE [Week] = 38
AND Sales_Num = 45281
AND Product IN ('PLA', 'PLA2')
GROUP BY Sales_Num, [Week]
I'm trying to get the last 13 week moving average of my inventory and sales columns (I have thousands of rows but I wanna get the last 13 week moving average from the recent going back since there's new data every week).
I have the weekno there (that is setup as YYYY-WEEKNO).
Anyone has any idea on how I can do this in Oracle SQL?
| weekno | inventory | sales |
| 202111| 5 | 78 |
| 202110| 6 | 50 |
| 202109| 3 | 80 |
| 202108| 2 | 75 |
| 202107| 5 | 33 |
| 202106| 8 | 77 |
| 202105| 3 | 80 |
| 202104| 2 | 75 |
| 202103| 5 | 33 |
| 202102| 8 | 77 |
| 202101| 8 | 77 |
| 202053| 2 | 75 |
| 202052| 5 | 33 |
| 202051| 8 | 77 |
| 202050| 8 | 77 |
..... and so on
You can use window functions. Assuming you have data for every week:
select t.*,
avg(inventory) over (order by weekno rows between 12 preceding and current row),
avg(sales) over (order by weekno rows between 12 preceding and current row)
from t;
Note: This assumes that previous 13 weeks includes the current week. If not, you would use:
avg(sales) over (order by weekno rows between 13 preceding and 1 preceding)
There is a table (SQL Server 2017) on sales of goods in stores, some records have no price.
+---------+-------------+---------+----------+-------+
| year_id | week_number | good_id | store_id | price |
+---------+-------------+---------+----------+-------+
| 2019 | 6 | 140629 | 2 | 199 |
+---------+-------------+---------+----------+-------+
| 2019 | 8 | 140629 | 2 | NULL |
+---------+-------------+---------+----------+-------+
| 2017 | 40 | 137233 | 9 | 278 |
+---------+-------------+---------+----------+-------+
| 2017 | 35 | 137233 | 9 | NULL |
+---------+-------------+---------+----------+-------+
| 2017 | 37 | 137233 | 9 | NULL |
+---------+-------------+---------+----------+-------+
We would like to replace the missing values according to the following scheme: set the price value to the same as the good with this number (good_id) from the same store (store_id), but sold as far as possible in the nearest to the missing value date, for example:
+---------+-------------+---------+----------+-------+
| year_id | week_number | good_id | store_id | price |
+---------+-------------+---------+----------+-------+
| 2019 | 6 | 140629 | 2 | 199 |
+---------+-------------+---------+----------+-------+
| 2019 | 8 | 140629 | 2 | 199 |
+---------+-------------+---------+----------+-------+
| 2017 | 40 | 137233 | 9 | 278 |
+---------+-------------+---------+----------+-------+
| 2017 | 35 | 137233 | 9 | 278 |
+---------+-------------+---------+----------+-------+
| 2017 | 37 | 137233 | 9 | 278 |
+---------+-------------+---------+----------+-------+
So far made something like this, but this query contains mutually exclusive conditions, so it does not affect the rows:
UPDATE dataset
SET price = p.price
FROM dataset AS p
WHERE good_id = p.good_id
AND store_id = p.store_id
AND price IS NULL
AND p.price IS NOT NULL;
GO
You can use apply. This works if all years have 52 weeks:
update d
set price = d2.price
from dataset d cross apply
(select top (1) d2.*
from dataset d2
where d2.good_id = d.good_id and
d2.store_id = d.store_id and
d2.price is not null
order by abs( (d2.year_id * 52 + d2.week_id) - (d.year_id * 52 + d.week_id) )
) d2
where d.price is null;
The only issue is when the comparisons pass the year boundary and the previous year has 53 weeks. Depending on how you define years, you can convert the year/week combos in to dates and use direct date comparisons for the difference.
I have a table with customer_number, week, and sales. I need to check if there were 12 consecutive weeks of no sales for each customer and create a flag of 0/1.
I can check the last 12 weeks or a certain time frame, but what's the best way to check for consecutive runs? Here is the code I have so far:
select * from weekly_sales
where customer_nbr in (123, 234)
and week < '2015-11-01'
and week > '2014-11-01'
order by customer_nbr, week
;
Sql Fiddle Demo
Here is a simplify version only need a week_id and sales
SELECT S1.weekid start_week, MAX(S2.weekid) end_week, SUM (S2.sales)
FROM Sales S1
JOIN Sales S2
ON S2.weekid BETWEEN S1.weekid and S1.weekid + 11
WHERE S1.weekid BETWEEN 1 and 25 -- your search range
GROUP BY S1.weekid
Let me know if that work for you
OUTPUT
| start_week | end_week | |
|------------|----------|----|
| 1 | 12 | 12 |
| 2 | 13 | 8 |
| 3 | 14 | 3 |
| 4 | 15 | 2 |
| 5 | 16 | 0 | <-
| 6 | 17 | 0 | <- no sales for 12 week
| 7 | 18 | 0 | <-
| 8 | 19 | 4 |
| 9 | 20 | 9 |
| 10 | 21 | 11 |
| 11 | 22 | 15 |
| 12 | 23 | 71 |
| 13 | 24 | 78 |
| 14 | 25 | 86 |
| 15 | 25 | 86 | < - less than 12 week range
| 16 | 25 | 86 | < - below this line
| 17 | 25 | 86 |
| 18 | 25 | 86 |
| 19 | 25 | 86 |
| 20 | 25 | 82 |
| 21 | 25 | 77 |
| 22 | 25 | 75 |
| 23 | 25 | 71 |
| 24 | 25 | 15 |
| 25 | 25 | 8 |
Your final query should have
HAVING SUM (S2.sales) = 0
AND COUNT(*) = 12
Ummmmm...You could use between 'week' and 'week', and you can use too the "count(column)" in order to improve performance.
So you only have to compare if result is bigger than 0
I have a database with sample data represented by Table 1 below. How do I write an SQL query to display them in either Table 2 or Table 3 format?
Table 1 Table 2
Date | Value Year | Week | Total Value | % Change
------------+------- ------+-----+--|---------------|----------
19/12/2011 | 60 2012 | 1 | 295 | 656.41%
20/12/2011 | 49 2012 | 0 | 39 | -80.98%
21/12/2011 | 42 2012 | 52 | 205 | -41.76%
22/12/2011 | 57 2011 | 51 | 352 |
23/12/2011 | 88
24/12/2011 | 18 Table 3
25/12/2011 | 38 Year | Week | SUM1 | Year | Week | SUM2 | % Change
26/12/2011 | 16 ------+--------+--------+--------+--------+--------+-----------
27/12/2011 | 66 2012 | 1 | 295 | 2012 | 0 | 39 | 656.41%
28/12/2011 | 21 2012 | 0 | 39 | 2011 | 52 | 205 | -80.98%
29/12/2011 | 79 2011 | 52 | 205 | 2011 | 51 | 352 | -41.76%
30/12/2011 | 7 2011 | 51 | 352 |
31/12/2011 | 16
01/01/2012 | 39
02/01/2012 | 17
03/01/2012 | 86
04/01/2012 | 55
05/01/2012 | 82
06/01/2012 | 0
07/01/2012 | 9
08/01/2012 | 46
My preference would be to run 1 query to aggregate Table 1 to the year/week level and then do the "% change" in another language, depending on your environment. However, if you truly needed a SQL-only solution, you could do something like this.
create table t1 as
select year(Date) as year, week(Date) as week, sum(Value) as totalvalue
from table1
group by year(Date) as year, week(Date) as week
order by Date desc
;
select a.year, a.month, a.totalvalue,
(a.totalvalue-b.totalvalue)/b.totalvalue as pct_change
from (
select year, month, totalvalue,
case when week>1 then week-1 else 52 end as prevweek,
case when week>1 then year else year-1 end as prevyear
from t1
) a
left outer join t1 b
on a.prevweek=b.week and a.prevyear =b.year
;