Got it. You get to use another cool function:
min(...) keep (dense_rank first order by ...)
to do this! :)
The code you need is:
min(day_of_month) keep (
dense_rank first order by new_item_flag desc
) over (partition by item)
So... WTF is that doing?!
First this sorts your rows by the order by clause. It then preserves the rows with the first value in this data set. Because this orders by new_item_flag desc, this is the Y rows (if there are any) or the N rows (if there aren't).
It then passes these rows to min, finding the day_of_month of the first Y or N as appropriate.
OK, but how does that help?
You can stick with the partitioned outer join. And use the first_value method to ensure all the "missing" rows have this value.
Finally ensure that the day is >= this value you've calculated:
drop table sales_staging purge;
create table sales_staging as
select * from (
-- item 1:
select 1 item, 1 day_of_month, 20 qty_sold, 'N' new_item_flag from dual union all
select 1 item, 2 day_of_month, 0 qty_sold, 'N' new_item_flag from dual union all
select 1 item, 3 day_of_month, 10 qty_sold, 'N' new_item_flag from dual union all
select 1 item, 4 day_of_month, 20 qty_sold, 'N' new_item_flag from dual union all
select 1 item, 5 day_of_month, 20 qty_sold, 'N' new_item_flag from dual union all
select 1 item, 6 day_of_month, 20 qty_sold, 'N' new_item_flag from dual union all
select 1 item, 7 day_of_month, 35 qty_sold, 'N' new_item_flag from dual union all
select 1 item, 8 day_of_month, 40 qty_sold, 'N' new_item_flag from dual union all
select 1 item, 9 day_of_month, 20 qty_sold, 'N' new_item_flag from dual union all
select 1 item, 10 day_of_month, 2 qty_sold, 'N' new_item_flag from dual union all
-- item 2:
select 2 item, 1 day_of_month, 10 qty_sold, 'N' new_item_flag from dual union all
select 2 item, 2 day_of_month, 21 qty_sold, 'N' new_item_flag from dual union all
select 2 item, 3 day_of_month, 33 qty_sold, 'N' new_item_flag from dual union all
select 2 item, 7 day_of_month, 20 qty_sold, 'N' new_item_flag from dual union all -- days 4,5,6 are missing! so 20 is the total sales qty for days 4,5,6 and 7
select 2 item, 8 day_of_month, 14 qty_sold, 'N' new_item_flag from dual union all
select 2 item, 9 day_of_month, 20 qty_sold, 'N' new_item_flag from dual union all
select 2 item, 10 day_of_month, 20 qty_sold, 'N' new_item_flag from dual union all
-- item 3 -> flagged as new item in the store starting from day 4
select 3 item, 4 day_of_month, 20 qty_sold, 'Y' new_item_flag from dual union all
select 3 item, 5 day_of_month, 20 qty_sold, 'Y' new_item_flag from dual union all
select 3 item, 6 day_of_month, 20 qty_sold, 'Y' new_item_flag from dual union all
/*
I only want to deal with missing days 7 and 8, since it will be assumed
that the item just arrived at the stores at the 4th of the month
*/
select 3 item, 9 day_of_month, 30 qty_sold, 'Y' new_item_flag from dual union all
select 3 item, 10 day_of_month, 2 qty_sold, 'Y' new_item_flag from dual
);
with rws as (
select rownum dy from dual connect by level <= 10
), stg as (
select ss.*,
day_of_month - lag(day_of_month, 1) over (
partition by item order by day_of_month
) missing_days,
min(day_of_month) keep (
dense_rank first order by new_item_flag desc
) over (partition by item) mn_dy
from sales_staging ss
), qty as (
select ss.*, rws.dy,
first_value(qty_sold) ignore nulls over (
partition by item order by dy
rows between current row and unbounded following
) nv,
first_value(missing_days) ignore nulls over (
partition by item order by dy
rows between current row and unbounded following
) mdays,
min(mn_dy) over (partition by item) mndy
from rws
left join stg ss partition by (item)
on dy = day_of_month
)
select dy, mndy, nv,
round(nv / case when mdays = 0 then 1 else mdays end, 2) qty
from qty q
where dy >= mndy
order by item , dy;
DY MNDY NV QTY
---------- ---------- ---------- ----------
1 1 20 20
2 1 0 0
3 1 10 10
4 1 20 20
5 1 20 20
6 1 20 20
7 1 35 35
8 1 40 40
9 1 20 20
10 1 2 2
1 1 10 10
2 1 21 21
3 1 33 33
4 1 20 5
5 1 20 5
6 1 20 5
7 1 20 5
8 1 14 14
9 1 20 20
10 1 20 20
4 4 20 20
5 4 20 20
6 4 20 20
7 4 30 10
8 4 30 10
9 4 30 10
10 4 2 2
PS - I'm guessing the extra rows are supposed to be item 3, right? ;)