Сначала я присоединюсь к доступному году / месяцу и пользователям, которые смогут сгенерировать строки. Затем, union all
в подзапросе может использоваться для отмены открытия и закрытия информации (возможно, можно также использовать cross apply
).
Последний шаг - агрегация и вычисление окна:
select
u.userID,
d.dt year_month,
sum(coalesce(sum(no_open - no_closed), 0))
over(partition by u.userID order by d.dt) no_cases_still_open,
sum(coalesce(sum(value_open - value_closed), 0))
over(partition by u.userID order by d.dt) value_open,
coalesce(sum(no_closed), 0) no_closed,
coalesce(sum(value_closed), 0) value_closed
from
(select distinct datefromparts(year(openedOn), month(openedOn), 1) dt from mytable) d
cross join (select distinct userID from mytable) u
left join (
select userID, openedOn dt, 1 no_open, value value_open, 0 no_closed, 0 value_closed from mytable
union all
select userID, closedDate dt, 0, 0, 1, value from mytable
) t
on t.userID = u.userID
and t.dt >= d.dt and t.dt < dateadd(month, 1, d.dt)
group by u.userID, d.dt
Демонстрация на DB Fiddle :
userID | year_month | no_cases_still_open | value_open | no_closed | value_closed
:----- | :--------- | ------------------: | ---------: | --------: | -----------:
U1 | 2020-01-01 | 3 | 770 | 0 | 0
U1 | 2020-02-01 | 2 | 270 | 2 | 640
U1 | 2020-03-01 | 3 | 690 | 0 | 0
U1 | 2020-04-01 | 3 | 720 | 1 | 150
U1 | 2020-05-01 | 2 | 300 | 1 | 420
U2 | 2020-01-01 | 0 | 0 | 1 | 100
U2 | 2020-02-01 | 1 | 790 | 0 | 0
U2 | 2020-03-01 | 1 | 790 | 1 | 640
U2 | 2020-04-01 | 2 | 1110 | 0 | 0
U2 | 2020-05-01 | 1 | 790 | 1 | 320
U3 | 2020-01-01 | 1 | 350 | 0 | 0
U3 | 2020-02-01 | 1 | 350 | 0 | 0
U3 | 2020-03-01 | 2 | 1160 | 0 | 0
U3 | 2020-04-01 | 2 | 1160 | 0 | 0
U3 | 2020-05-01 | 3 | 1390 | 0 | 0
U4 | 2020-01-01 | 0 | 0 | 0 | 0
U4 | 2020-02-01 | 2 | 680 | 0 | 0
U4 | 2020-03-01 | 1 | 490 | 1 | 190
U4 | 2020-04-01 | 2 | 700 | 0 | 0
U4 | 2020-05-01 | 1 | 490 | 1 | 210