SQL query for incoming and outgoing stocks, first and last - sql

I need to make a query that shows sales and stocks (incoming and outgoing) for each model in October 2021.
The point is that for obtaining incoming and outgoing stocks I need to get vt_stocks_cube_sz.qty respectively for the first day of month and for the last day of month .
Now I wrote just sum of stocks (SUM(vt_stocks_cube_sz.qty) as stocks) but it isn't correct.
Could you help me to split the stocks according to the rule above, I cannot understant how to write the query correctly.
%%time
SELECT vt_sales_cube_sz.modc_barc2 model,
SUM(vt_sales_cube_sz.qnt) sales,
SUM(vt_stocks_cube_sz.qty) as stocks
FROM vt_sales_cube_sz
LEFT JOIN vt_date_cube2
ON vt_sales_cube_sz.id_calendar_int = vt_date_cube2.id_calendar_int
LEFT JOIN vt_stocks_cube_sz ON
vt_stocks_cube_sz.parent_modc_barc = vt_sales_cube_sz.modc_barc AND
vt_stocks_cube_sz.id_stock = vt_sales_cube_sz.id_stock AND
vt_stocks_cube_sz.id_calendar_int = vt_sales_cube_sz.id_calendar_int AND
vt_stocks_cube_sz.vipusk_type = vt_sales_cube_sz.price_type
WHERE vt_date_cube2.wk_year_id = 2021
AND vt_date_cube2.wk_MoY_id = 10
AND vt_sales_cube_sz.id_stock IN
(SELECT id_stock
FROM vt_warehouse_cube
WHERE channel = \'OffLine\')
GROUP BY vt_sales_cube_sz.modc_barc2

If you're looking for a robust and generalizable approach I'd suggest using analytic functions such as FIRST_VALUE, LAST_VALUE or something slightly different with RANK or ROW_NUMBER.
A simple example follows, so you can rerun it on your side and adjust it to the specific tables/fields you're using.
N.B.: You might need some tiebreakers in case you had multiple entries for the same first/last day.
with dummy_table as (
SELECT 1 as month, 1 as day, 10 as value UNION ALL
SELECT 1 as month, 2 as day, 20 as value UNION ALL
SELECT 1 as month, 3 as day, 30 as value UNION ALL
SELECT 2 as month, 1 as day, 5 as value UNION ALL
SELECT 2 as month, 3 as day, 15 as value UNION ALL
SELECT 2 as month, 5 as day, 25 as value
)
SELECT
month,
day,
case when day = first_day then 'first' else 'last' end as type,
value,
FROM (
SELECT *
, FIRST_VALUE(day) over (partition by month order by day ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) as first_day
, LAST_VALUE(day) over (partition by month order by day ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) as last_day
FROM dummy_table
) tmp
WHERE day = first_day OR day=last_day
Dummy table:
Row
month
day
value
1
1
1
10
2
1
2
20
3
1
3
30
4
2
1
5
5
2
3
15
6
2
5
25
Result:
Row
month
day
type
value
1
1
1
first
10
2
1
3
last
30
3
2
1
first
5
4
2
5
last
25

Related

SQL (Redshift) - Rolling average with all preceding values

I have a table like so:
Day
Value
1
3
1
5
1
1
2
4
2
7
3
1
3
1
3
2
3
5
How do I create a rolling average that takes into account all previous days to produce a table like so:
Day
Rolling_avg
1
3
2
4
3
3.22
Day1 = avg(day 1 values)
Day2 = avg(day1 + day2 values)
Day3 = avg(day1 + day2 + day3 values)
so on so forth..thank you!
First aggregate by day to get the sum of values and counts for each day. Then use analytic functions to find the rolling averages.
WITH cte AS (
SELECT Day, SUM(Value) ValueSum, COUNT(*) AS Count
FROM yourTable
GROUP BY Day
)
SELECT Day, SUM(ValueSum) OVER (ORDER BY Day) /
SUM(Count) OVER (ORDER BY Day) AS Rolling_avg
FROM cte
ORDER BY Day;
Demo

Running cumulative count group by month

I have an table with values like this:
count month-year
6 12-2020
5 12-2020
4 11-2020
3 11-2020
3 10-2020
2 10-2020
2 09-2020
1 09-2020
I want to group the data by the month and show the sum of the count for the current month and the months before it. I am expecting the following output:
count month-year
26 12-2020 <- month 12 count equal to month 12 sum + count start from month 9
15 11-2020 <- month 11 count equal to month 11 sum + count start from month 9
8 10-2020 <- month 10 count equal to month 9 sum + month 10
3 09-2020 <- assume month 9 is the launch month, count = sum count of month 9
You want to use SUM here twice, both as an aggregate and as an analytic function:
SELECT
[month-year],
SUM(SUM(count)) OVER (ORDER BY [month-year]) AS count
FROM yourTable
GROUP BY
[month-year]
ORDER BY
[month-year] DESC;
Demo
There is another way to calculate the desired result
select Distinct [month-year] ,
SUM(count) OVER (ORDER BY [month-year]) AS count
from yourTable
order by [month-year] desc

rolling sum over the last 7 days, how to include missing dates - postresql

I am trying to get something like this but I have only the first two columns:
dates sales rolling_sum7days
01-01-2019 1 1
02-01-2019 3 4
03-01-2019 5 9
04-01-2019 2 11
05-01-2019 7 18
06-01-2019 8 26
08-01-2019 10 35
09-01-2019 1 32
10-01-2019 8 39
I have come up with this but have not find a way to deal with missing values such as the sales for 07-01-2019
SELECT dates
sum(sales) over(order by dates ROWS BETWEEN 6 preceding AND CURRENT ROW)
from table
What should I correct?
found some similar problems but they did not solved this issue.
for example: this
demo:db<>fiddle
SELECT
gs::date as dates,
COALESCE(mt.sales, 0) AS sales,
sum(sales) over(order by gs ROWS BETWEEN 6 preceding AND CURRENT ROW)
FROM
mytable mt
RIGHT OUTER JOIN
generate_series('2019-01-01', '2019-01-11', interval '1 day') gs
ON gs = mt.dates
To fill missing dates within a certain range you can use generate_series() for this date range and do an outer join.
If you do not want to fix the gs parameters then, of course, you could calculate them before, e.g. taking the MIN and MAX from your table:
demo:db<>fiddle
WITH date_bounds AS (
SELECT min(dates), max(dates) FROM mytable
)
SELECT
gs::date as dates,
COALESCE(mt.sales, 0) AS sales,
sum(sales) over(order by gs ROWS BETWEEN 6 preceding AND CURRENT ROW)
FROM
mytable mt
RIGHT OUTER JOIN
generate_series(
(SELECT min FROM date_bounds),
(SELECT max FROM date_bounds),
interval '1 day'
) gs
ON gs = mt.dates
Postgres supports window ranges with intervals. So you can do what you want very simply:
SELECT t.*,
SUM(t.sales) OVER (ORDER BY t.dates
RANGE BETWEEN interval '6 day' preceding AND current row
) as rolling_sum7days
FROM t;
There is no need to expand the data to every day. That just slows down the query.

How to pick one non null date from dates - if date is null pick next one

I need to pick one date from week, it has to be Friday. However, when Friday is null - it means no data was entered, and I have to find any other day with data in the same week. Can someone share their views on how to solve this type of situation?
If you see in the following data, in the 2nd week, Friday has null entry, so another day has to be picked up.
Day Weekdate Data entry dt Data
1 2/7/2016
2 2/8/2016
3 2/9/2016
4 2/10/2016
5 2/11/2016
6 2/12/2016 2/12/2016 500
7 2/13/2016
1 2/14/2016
2 2/15/2016
3 2/16/2016
4 2/17/2016 2/17/2016 300
5 2/18/2016
6 2/19/2016 NULL NULL
7 2/20/2016
1 2/21/2016
2 2/22/2016
3 2/23/2016
4 2/24/2016
5 2/25/2016
6 2/26/2016 2/26/2016 250
7 2/27/2016
You may try this
--Not null data
select * from tblData
where DATEPART(dw,weekDate) = 6 and data is not null
Union
Select data.* from
(
select weekDate
from tblData
where DATEPART(dw,weekDate) = 6 and data is null
) nullData --Select Friday with null data
Cross Apply
(
--Find first record with not null data that is within this week
Select top 1 *
From tblData data
Where
data.weekDate between Dateadd(day, -6, nullData.weekDate) and nullData.weekDate
and data.data is not null
Order by data.weekDate desc
) data
You can try something like this to get the data entered for the latest date (Friday first, then every other day) for each week in your table:
SELECT
Weeks.FirstofWeek,
Detail.Day,
Detail.DataEntryDt,
Detail.Data
FROM
( --master list of weeks
SELECT DISTINCT DATEADD(DAY,(1-DATEPART(dw,Weekdate)),Weekdate) AS FirstofWeek
FROM dataTable
) AS Weeks
LEFT OUTER JOIN
( --detail
SELECT
--order first by presence of data, then by date, selecting Friday first:
ROW_NUMBER() OVER (PARTITION BY DATEADD(DAY,(1-DATEPART(dw,Weekdate)),Weekdate) ORDER BY CASE WHEN Data IS NOT NULL THEN 99 ELSE 0 END DESC, CASE WHEN [Day] = 6 THEN 99 ELSE [Day] END DESC) AS RowNum,
[Day],
DATEADD(DAY,(1-DATEPART(dw,Weekdate)),Weekdate) AS FirstofWeek,
Weekdate,
DataEntryDt,
Data
FROM dataTable
) AS Detail
ON Weeks.FirstofWeek = Detail.FirstofWeek
AND Detail.RowNum = 1 --get only top record for week with data present

How to calculate moving sum with reset based on condition in teradata SQL?

I have this data and I want to sum the field USAGE_FLAG but reset when it drops to 0 or moves to a new ID keeping the dataset ordered by SU_ID and WEEK:
SU_ID WEEK USAGE_FLAG
100 1 0
100 2 7
100 3 7
100 4 0
101 1 0
101 2 7
101 3 0
101 4 7
102 1 7
102 2 7
102 3 7
102 4 0
So I want to create this table:
SU_ID WEEK USAGE_FLAG SUM
100 1 0 0
100 2 7 7
100 3 7 14
100 4 0 0
101 1 0 0
101 2 7 7
101 3 0 0
101 4 7 7
102 1 7 7
102 2 7 14
102 3 7 21
102 4 0 0
I have tried the MSUM() function using GROUP BY but it won't keep the order I want above. It groups the 7's and the week numbers together which I don't want.
Anyone know if this is possible to do? I'm using teradata
In standard SQL a running sum can be done using a windowing function:
select su_id,
week,
usage_flag,
sum(usage_flag) over (partition by su_id order by week) as running_sum
from the_table;
I know Teradata supports windowing functions, I just don't know whether it also supports an order by in the window definition.
Resetting the sum is a bit more complicated. You first need to create "group IDs" that change each time the usage_flag goes to 0. The following works in PostgreSQL, I don't know if this works in Teradata as well:
select su_id,
week,
usage_flag,
sum(usage_flag) over (partition by su_id, group_nr order by week) as running_sum
from (
select t1.*,
sum(group_flag) over (partition by su_id order by week) as group_nr
from (
select *,
case
when usage_flag = 0 then 1
else 0
end as group_flag
from the_table
) t1
) t2
order by su_id, week;
Try below code, with use of RESET function it is working fine.
select su_id,
week,
usage_flag,
SUM(usage_flag) OVER (
PARTITION BY su_id
ORDER BY week
RESET WHEN usage_flag < /* preceding row */ SUM(usage_flag) OVER (
PARTITION BY su_id ORDER BY week
ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING)
ROWS UNBOUNDED PRECEDING
)
from emp_su;
Please try below SQL:
select su_id,
week,
usage_flag,
SUM(usage_flag) OVER (PARTITION BY su_id ORDER BY week
RESET WHEN usage_flag = 0
ROWS UNBOUNDED PRECEDING
)
from emp_su;
Here RESET WHEN usage_flag = 0 will reset sum whenever sum usage_flag drops to 0