Как присоединить таблицy dim_date для появления first_of_month_date в общей таблице

Рейтинг: 0Ответов: 1Опубликовано: 28.03.2023

У меня есть 2 таблицы, для пустых строк нужно добавить верхнее значение. Проблема в том, что после объединения с таблицей dim_date даты пропущенных дат в общем запросе не отображаются. Например, именно для опреленного id я получила строки, но если без, я не могу. https://stackoverflow.com/questions/75863208/how-to-join-dim-date-table-for-appears-first-of-month-date

select first_day_of_month, last_value(c_loan_agreement_id) IGNORE NULLS OVER (ORDER BY first_day_of_month ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)  c_loan_agreement_id from business_core.dim_date dd
left join business_core.fact_loan_agreement fla on date_trunc('month',fla.date_term_date)  = dd.first_day_of_month and dd.first_day_of_month <= '2023-12-01' and dd.first_day_of_month>= '2022-12-01' --and fla.c_loan_agreement_id= 'ALB000ab242-d005-4dc0-bf05-58dda00658a7'
where dd.first_day_of_month <= '2023-12-01' and dd.first_day_of_month>= '2022-12-01' 
group by  c_loan_agreement_id, first_day_of_month ```


etoo adoptivnoe query s uchetom commentarija

select first_day_of_month, date_trunc('month',nextdate::date) as nextdate ,  last_value(c_loan_agreement_id) IGNORE NULLS OVER (ORDER BY first_day_of_month ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)  c_loan_agreement_id from business_core.dim_date dd
left join ( select c_loan_agreement_id, date_trunc('month', frr.repayment_date)::date as BOP, date_trunc('month',lead(frr.repayment_date,1)over(partition by c_loan_agreement_id order by frr.repayment_date)) as nextdate from  business_core.fact_receivable_repayment frr group by c_loan_agreement_id,bop,  repayment_date ) frr  on frr.bop <= dd.first_day_of_month and nextdate>dd.first_day_of_month 
where dd.first_day_of_month <= current_date and dd.first_day_of_month>= '2022-12-01' 
group by  c_loan_agreement_id, first_day_of_month, nextdate;  ```

[![введите сюда описание изображения][1]][1]
[![введите сюда описание изображения][2]][2]


  [1]: https://i.sstatic.net/iRGje.png
  [2]: https://i.sstatic.net/UjnX5.png

Ответы

▲ 1Принят

Посмотрите пример соединения таблицы с календарем. Календарь создан "на ходу", рекурсивным перебором дат от мин до макс даты.
Для соединения другой таблицы с календарем, в целевой таблице выбираем текущую и следующую дату 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 так и осталась неизвестной;)