Посмотрите пример соединения таблицы с календарем. Календарь создан "на ходу", рекурсивным перебором дат от мин до макс даты.
Для соединения другой таблицы с календарем, в целевой таблице выбираем текущую и следующую дату lead(EventDate,1,EventDate)over(partition by id order by EventDate) nextdate
.
Соединение с календарем должно быть от текущей даты до следующей. Т.е. соединяем текущую строку и календарь, поэтому значения текущей строки наследуются и в добавляемые (соединяемые) строки.
UPD. Добавлю тестовые данные, чтобы были разные Id. Запрос отработает правильно при разных Id, в том числе когда диапазоны пересекается.
WITH src AS (
SELECT id,cast(EventDate as date) EventDate, value
FROM( VALUES
(4, '2022-12-30', 100.0)
,(4, '2023-01-04', 200.0)
,(4, '2023-01-07', 300)
,(5, '2023-01-04', 250)
,(5, '2023-01-07', 350)
) t (id,EventDate,Value)
)
,dim_date as (
select 0 n,dateadd(d,0,(select min(EventDate) from src)) day_of_month
, (select datediff(d,min(EventDate),max(EventDate)) from src) maxn
union all
select n+1,dateadd(d,1,day_of_month),maxn from dim_date where n<maxn
)
,data as(
select id,EventDate,value
,lead(EventDate,1,EventDate)over(partition by id order by EventDate) nextdate
from src
)
SELECT t.id
,case when coalesce(n,0)=0 then EventDate
else m.day_of_month
end EventDate
,t.value
,m.n,nextdate,EventDate EventDate2 ,m.day_of_month cdate
FROM data t
left JOIN dim_date m on t.EventDate<=m.day_of_month and t.nextdate>m.day_of_month
order by id,EventDate
option (maxrecursion 500)
Пример результата запроса
ID |
EventDate |
value |
n |
nextdate |
EventDate2 |
day_of_month |
4 |
2022-12-30 |
100.0 |
0 |
2023-01-04 |
2022-12-30 |
2022-12-30 |
4 |
2022-12-31 |
100.0 |
1 |
2023-01-04 |
2022-12-30 |
2022-12-31 |
4 |
2023-01-01 |
100.0 |
2 |
2023-01-04 |
2022-12-30 |
2023-01-01 |
4 |
2023-01-02 |
100.0 |
3 |
2023-01-04 |
2022-12-30 |
2023-01-02 |
4 |
2023-01-03 |
100.0 |
4 |
2023-01-04 |
2022-12-30 |
2023-01-03 |
4 |
2023-01-04 |
200.0 |
5 |
2023-01-07 |
2023-01-04 |
2023-01-04 |
4 |
2023-01-05 |
200.0 |
6 |
2023-01-07 |
2023-01-04 |
2023-01-05 |
4 |
2023-01-06 |
200.0 |
7 |
2023-01-07 |
2023-01-04 |
2023-01-06 |
4 |
2023-01-07 |
300.0 |
null |
2023-01-07 |
2023-01-07 |
null |
5 |
2023-01-04 |
250.0 |
5 |
2023-01-07 |
2023-01-04 |
2023-01-04 |
5 |
2023-01-05 |
250.0 |
6 |
2023-01-07 |
2023-01-04 |
2023-01-05 |
5 |
2023-01-06 |
250.0 |
7 |
2023-01-07 |
2023-01-04 |
2023-01-06 |
5 |
2023-01-07 |
350.0 |
null |
2023-01-07 |
2023-01-07 |
null |
Возможно, Вам больше подойдет такой вариант, когда нужны остатки не по дням, а начало месяца:
WITH src AS (
SELECT id,cast(EventDate as date) EventDate, value
FROM( VALUES
(4, '2022-12-30', 100.0)
,(4, '2023-01-04', 200.0)
,(4, '2023-01-07', 300)
,(4, '2023-02-08', 201)
,(5, '2023-01-04', 250)
,(5, '2023-03-07', 350)
,(5, '2023-04-05', 350)
) t (id,EventDate,Value)
)
,dim_date as (
select 0 n,datefromparts((select year(min(EventDate)) from src)
,(select month(min(EventDate)) from src)
,1) first_day_of_month
, (select eomonth(max(EventDate)) from src) maxDate
union all
select n+1,dateadd(mm,1,first_day_of_month),maxDate from dim_date
where dateadd(m,1,first_day_of_month)<=maxDate
)
,data as(
select id,EventDate,value
,lead(EventDate,1,EventDate)over(partition by id order by EventDate) nextdate
from src
)
--select * from dim_date
SELECT t.id
,case when EventDate>first_day_of_month then EventDate
else m.first_day_of_month
end EventDate
,t.value
,m.n,nextdate,EventDate EventDate0 ,m.first_day_of_month first_day
FROM data t
left JOIN dim_date m
on
( first_day_of_month between eventdate and nextdate )
or
( month(t.EventDate)=month(first_day_of_month) )
order by id,EventDate
option (maxrecursion 500)
Результат запроса на этих тестовых данных
id |
EventDate |
value |
n |
nextdate |
EventDate0 |
first_day |
4 |
2022-12-30 |
100.0 |
0 |
2023-01-04 |
2022-12-30 |
2022-12-01 |
4 |
2023-01-01 |
100.0 |
1 |
2023-01-04 |
2022-12-30 |
2023-01-01 |
4 |
2023-01-04 |
200.0 |
1 |
2023-01-07 |
2023-01-04 |
2023-01-01 |
4 |
2023-01-07 |
300.0 |
1 |
2023-02-08 |
2023-01-07 |
2023-01-01 |
4 |
2023-02-01 |
300.0 |
2 |
2023-02-08 |
2023-01-07 |
2023-02-01 |
4 |
2023-02-08 |
201.0 |
2 |
2023-02-08 |
2023-02-08 |
2023-02-01 |
5 |
2023-01-04 |
250.0 |
1 |
2023-03-07 |
2023-01-04 |
2023-01-01 |
5 |
2023-02-01 |
250.0 |
2 |
2023-03-07 |
2023-01-04 |
2023-02-01 |
5 |
2023-03-01 |
250.0 |
3 |
2023-03-07 |
2023-01-04 |
2023-03-01 |
5 |
2023-03-07 |
350.0 |
3 |
2023-04-05 |
2023-03-07 |
2023-03-01 |
5 |
2023-04-01 |
350.0 |
4 |
2023-04-05 |
2023-03-07 |
2023-04-01 |
5 |
2023-04-05 |
350.0 |
4 |
2023-04-05 |
2023-04-05 |
2023-04-01 |
Пример
Другой пример, для случая, когда последняя добавленная запись должна быть началом следующего месяца от последней записи в таблице.
WITH src AS (
SELECT id,cast(EventDate as date) EventDate, value
FROM( VALUES
(4, '2022-12-30', 100.0)
,(4, '2023-01-04', 200.0)
,(4, '2023-01-07', 300)
,(4, '2023-02-08', 201)
,(5, '2023-01-04', 250)
,(5, '2023-03-31', 350)
,(5, '2023-04-01', 360)
) t (id,EventDate,Value)
)
,dim_date as (
select 0 n,datefromparts((select year(min(EventDate)) from src)
,(select month(min(EventDate)) from src)
,1) first_day_of_month
, dateadd(d,1,(select eomonth(max(EventDate)) from src)) maxDate
union all
select n+1,dateadd(mm,1,first_day_of_month),maxDate from dim_date
where dateadd(m,1,first_day_of_month)<=maxDate
)
,data as(
select id,EventDate,value
,lead(EventDate)over(partition by id order by EventDate) nextdate
from src
)
SELECT t.id
,case when EventDate>first_day_of_month then EventDate
else m.first_day_of_month
end EventDate
,t.value
,m.n,nextdate,EventDate EventDate0 ,m.first_day_of_month first_day
FROM data t
left JOIN dim_date m
on
( first_day_of_month between eventdate and nextdate )
or
( month(t.EventDate)=month(first_day_of_month) )
or
( first_day_of_month=dateadd(d,1,eomonth(eventdate)) and nextdate is null)
order by id,EventDate
option (maxrecursion 500)
Результат запроса на этих тестовых данных
d |
EventDate |
value |
n |
nextdate |
EventDate0 |
first_day |
4 |
2022-12-30 |
100.0 |
0 |
2023-01-04 |
2022-12-30 |
2022-12-01 |
4 |
2023-01-01 |
100.0 |
1 |
2023-01-04 |
2022-12-30 |
2023-01-01 |
4 |
2023-01-04 |
200.0 |
1 |
2023-01-07 |
2023-01-04 |
2023-01-01 |
4 |
2023-01-07 |
300.0 |
1 |
2023-02-08 |
2023-01-07 |
2023-01-01 |
4 |
2023-02-01 |
300.0 |
2 |
2023-02-08 |
2023-01-07 |
2023-02-01 |
4 |
2023-02-08 |
201.0 |
2 |
null |
2023-02-08 |
2023-02-01 |
4 |
2023-03-01 |
201.0 |
3 |
null |
2023-02-08 |
2023-03-01 |
5 |
2023-01-04 |
250.0 |
1 |
2023-03-31 |
2023-01-04 |
2023-01-01 |
5 |
2023-02-01 |
250.0 |
2 |
2023-03-31 |
2023-01-04 |
2023-02-01 |
5 |
2023-03-01 |
250.0 |
3 |
2023-03-31 |
2023-01-04 |
2023-03-01 |
5 |
2023-03-31 |
350.0 |
3 |
2023-04-01 |
2023-03-31 |
2023-03-01 |
5 |
2023-04-01 |
350.0 |
4 |
2023-04-01 |
2023-03-31 |
2023-04-01 |
5 |
2023-04-01 |
360.0 |
4 |
null |
2023-04-01 |
2023-04-01 |
5 |
2023-05-01 |
360.0 |
5 |
null |
2023-04-01 |
2023-05-01 |
При выборке следующей даты, используем признак отстутвия следующей записи по данному Id - null в поле nextdate.
Также имеется 2 записи в результате с датой 2023-04-01 а)на начало месяца б)за этот день.
Структрура и назначение Вашей талблицы dim_date так и осталась неизвестной;)