Немного опоздал, но искал решение для аналогичной проблемы и не нашел ответа, поэтому попытался его найти.
Одним из решений может быть попытка настроить первый набор данных, чтобы он соответствовал второму набору, и на последнем шаге мы объединяем два набора, скорректированные данные с данными наложения.
Так что нам нужно настроить набор по умолчанию, и это потребует нескольких шагов работы.
Предположения: -
Периоды по умолчанию не накладываются друг на друга.
Периоды перекрытия не перекрываются самим собой.
Данные перекрытия должны иметь разрывы, превышающие один день, чтобы гарантировать, что любые корректировки не повлияют на другой период наложения; мы не потеряем связанные значения наложения, так как вернем их на последнем шаге.
Для периодов по умолчанию, которые частично перекрываются (перекрытие больше или равно периоду по умолчанию)
Если дата начала периода перекрывается с любым периодом наложения, мы изменим дату начала на конец периода наложения плюс один день
Default: |<-------- P1 ------>|
Overlap: |<-----------O1------>|
=================================================================
Output: |<-----P1 ---->|
Если дата окончания периода перекрывается с каким-либо периодом наложения, мы изменим дату окончания на начало периода наложения минус один день
Default: |<-------- P1 ------>|
Overlap: |<-----------O1------>|
===================================================================
Output: |<--- P1 ---->|
Удалить любой период по умолчанию, если он полностью покрыт периодом перекрытия
Default: |<--- P1 --->|
Overlap: |<--------O1------>|
===================================================================
Output: nothing
Для периодов по умолчанию, которые разбиты перекрывающимися периодами (перекрытие меньше, чем периоды)
Получить первую часть (от начала периода до первого периода перекрытия)
Получите промежуточные части, то есть от конца любого перекрывающегося периода до начала следующего перекрывающегося периода.
Получить последнюю часть (от последнего перекрытия до конца периода)
Default: |<---------------------------- P1 ------------------------------>|
Overlap: |<---O1--->| |<---O2-->| |<--O3->|
===================================================================
Output: |<-P1->| |<-P1->| |<-P1->| |<-P1->|
Наконец, объединить данные и получить результат
За притирочные типы, которые мы рассматриваем
Default: |<---- P1 --->|
Overlap: |<-----------O1------>|
===================================================================
Output: |<---- P1 --->||<-----------O1------>|
Позволяет построить T-SQL
With [System] as (
Select 1 [RowNum],cast('Jan 01,2017' as date) [StartDate],dateadd(day,-1,cast('Feb 01,2017' as date)) [EndDate],0500 [Value] union all
Select 2 [RowNum],cast('Feb 01,2017' as date) [StartDate],dateadd(day,-1,cast('Mar 01,2017' as date)) [EndDate],0700 [Value] union all
Select 3 [RowNum],cast('Mar 01,2017' as date) [StartDate],dateadd(day,-1,cast('Apr 01,2017' as date)) [EndDate],0900 [Value] union all
Select 4 [RowNum],cast('Apr 01,2017' as date) [StartDate],dateadd(day,-1,cast('May 01,2017' as date)) [EndDate],0700 [Value] union all
Select 5 [RowNum],cast('May 01,2017' as date) [StartDate],dateadd(day,-1,cast('Jun 01,2017' as date)) [EndDate],0900 [Value] union all
Select 6 [RowNum],cast('Jun 01,2017' as date) [StartDate],dateadd(day,-1,cast('Jul 01,9999' as date)) [EndDate],1500 [Value]
),Overrides as (
Select 1 [RowNum],cast('Feb 12,2017' as date) [StartDate],cast('Mar 25,2017' as date) [EndDate],1 [Value] union all
Select 2 [RowNum],cast('Mar 28,2017' as date) [StartDate],cast('May 15,2017' as date) [EndDate],2 [Value] union all
Select 3 [RowNum],cast('May 18,2017' as date) [StartDate],cast('May 20,2017' as date) [EndDate],3 [Value] union all
Select 4 [RowNum],cast('Jun 05,2017' as date) [StartDate],cast('Jun 08,2017' as date) [EndDate],4 [Value] union all
Select 5 [RowNum],cast('Jun 09,2017' as date) [StartDate],cast('Jun 16,2017' as date) [EndDate],5 [Value] union all
Select 6 [RowNum],cast('Jun 17,2017' as date) [StartDate],cast('Jun 22,2017' as date) [EndDate],6 [Value] union all
Select 7 [RowNum],cast('Jun 23,2017' as date) [StartDate],cast('Jun 27,2017' as date) [EndDate],7 [Value]
),PrepareOverridePeriods as (--if override periods have no gabs betwwen we need to merge them
Select p1.StartDate, p1.EndDate
from Overrides p1
left join Overrides p2 on p1.StartDate = DATEADD(day,1,p2.EndDate)
where p2.StartDate is null
union all
select p1.StartDate,p2.EndDate
from PrepareOverridePeriods p1
inner join Overrides p2 on p1.EndDate = DATEADD(day,-1,p2.StartDate)
),OverridePeriods as (
select ROW_NUMBER() over (order by StartDate) [RowNum],StartDate,MAX(EndDate) as EndDate
from PrepareOverridePeriods group by StartDate
),AdjustedPeriods as (
select s.RowNum,'Adj.' [type]
,isnull(dateadd(day,1,ShiftRight.EndDate),s.StartDate) [StartDate]
,isnull(dateadd(day,-1,ShiftLeft.StartDate),s.EndDate) [EndDate]
,s.Value
from System s
left outer join OverridePeriods ShiftRight on s.StartDate between ShiftRight.StartDate and ShiftRight.EndDate
left outer join OverridePeriods ShiftLeft on s.EndDate between ShiftLeft.StartDate and ShiftLeft.EndDate
left outer join OverridePeriods RemovePeriod on s.StartDate between RemovePeriod.StartDate and RemovePeriod.EndDate and s.EndDate between RemovePeriod.StartDate and RemovePeriod.EndDate
where RemovePeriod.StartDate is null
),SmallOverrides as ( --TODO: change SystemCalculated to AdjustSystemCalculatedPeriods
select ROW_NUMBER() over (partition by s.RowNum order by o.StartDate ) [RowNum],
o.RowNum [OverrideRowNum],o.StartDate [OverrideStartDate],o.EndDate [OverrideEndDate],s.Value [Value]
,s.RowNum [SystemRowNum],s.StartDate [SystemStartDate],s.EndDate [SystemEndDate]
from OverridePeriods o
inner join AdjustedPeriods s on o.StartDate between s.StartDate and s.EndDate and o.EndDate between s.StartDate and s.EndDate
)
--,FirstAndLastParts as (
--select [SystemRowNum],[type]
-- ,case when [type]='First' then min([SystemStartDate]) else dateadd(day,1,max(OverrideEndDate)) end [StartDate]
-- ,case when [type]='First' then dateadd(day,-1,min(OverrideStartDate)) else max([SystemEndDate]) end [EndDate]
-- ,min(Value) [Value]
-- from (select *,'First' [type] from SmallOverrides o union all
-- select *,'Last' [type] from SmallOverrides o) data
-- group by [SystemRowNum],[type]
--)
,FirstParts as (
select [SystemRowNum],'First' [type]
,min([SystemStartDate]) [StartDate]
,dateadd(day,-1,min(OverrideStartDate)) [EndDate]
,min(Value) [Value]
from SmallOverrides
group by [SystemRowNum]
),LastParts as (
select [SystemRowNum],'Last' [type]
,dateadd(day,1,max(OverrideEndDate)) [StartDate]
,max([SystemEndDate]) [EndDate]
,min(Value) [Value]
from SmallOverrides
group by [SystemRowNum]
),IntermediatParts as (
select s.SystemRowNum [RowNum],'Inter.' [type]
,dateadd(day,1,s.OverrideEndDate) [StartDate]
,dateadd(day,-1,e.OverrideStartDate) [EndDate]
,s.Value
from SmallOverrides s
left outer join SmallOverrides e on e.SystemRowNum=s.SystemRowNum and s.RowNum+1=e.RowNum
where e.RowNum is not null --remove the first and lasts
),AdjustedPeriodsFiltered as (--remove blocks that are broken to smaller pieces
select s.*
from AdjustedPeriods s
left outer join OverridePeriods o on o.StartDate between s.StartDate and s.EndDate and o.EndDate between s.StartDate and s.EndDate
where o.StartDate is null
),AllParts as (
select * from IntermediatParts union all --order by SystemRowNum,OverrideStartDate
select * from FirstParts union all
select * from LastParts union all
select * from AdjustedPeriodsFiltered
),Merged as (
select [RowNum],[type] [Source],StartDate,EndDate,Value,'System' [RecordType] from AllParts
union all
select [RowNum],'override' [Source],StartDate,EndDate,Value,'Override' [RecordType] from Overrides
)
select * from Merged order by StartDate
Можем ли мы настроить набор данных по-другому, да, другой подход - получить все ожидаемые значения из дат начала и окончания для периодов по умолчанию и периодов наложения, а затем восстановить новый набор периодов это к значениям по умолчанию, объедините это с наложением, и мы получили это.
Те же самые предположения и шаги предприняты как ниже: -
With [System] as (
Select 1 [RowNum],cast('Jan 01,2017' as date) [StartDate],dateadd(day,-1,cast('Feb 01,2017' as date)) [EndDate],0500 [Value] union all
Select 2 [RowNum],cast('Feb 01,2017' as date) [StartDate],dateadd(day,-1,cast('Mar 01,2017' as date)) [EndDate],0700 [Value] union all
Select 3 [RowNum],cast('Mar 01,2017' as date) [StartDate],dateadd(day,-1,cast('Apr 01,2017' as date)) [EndDate],0900 [Value] union all
Select 4 [RowNum],cast('Apr 01,2017' as date) [StartDate],dateadd(day,-1,cast('May 01,2017' as date)) [EndDate],0700 [Value] union all
Select 5 [RowNum],cast('May 01,2017' as date) [StartDate],dateadd(day,-1,cast('Jun 01,2017' as date)) [EndDate],0900 [Value] union all
Select 6 [RowNum],cast('Jun 01,2017' as date) [StartDate],dateadd(day,-1,cast('Jul 01,9999' as date)) [EndDate],1500 [Value]
),Overrides as (
Select 1 [RowNum],cast('Feb 12,2017' as date) [StartDate],cast('Mar 25,2017' as date) [EndDate],1 [Value] union all
Select 2 [RowNum],cast('Mar 28,2017' as date) [StartDate],cast('May 15,2017' as date) [EndDate],2 [Value] union all
Select 3 [RowNum],cast('May 18,2017' as date) [StartDate],cast('May 20,2017' as date) [EndDate],3 [Value] union all
Select 4 [RowNum],cast('Jun 05,2017' as date) [StartDate],cast('Jun 08,2017' as date) [EndDate],4 [Value] union all
Select 5 [RowNum],cast('Jun 09,2017' as date) [StartDate],cast('Jun 16,2017' as date) [EndDate],5 [Value] union all
Select 6 [RowNum],cast('Jun 17,2017' as date) [StartDate],cast('Jun 22,2017' as date) [EndDate],6 [Value] union all
Select 7 [RowNum],cast('Jun 23,2017' as date) [StartDate],cast('Jun 27,2017' as date) [EndDate],7 [Value]
),PrepareOverridePeriods as (--if override periods have no gabs between we need to merge them
Select p1.StartDate, p1.EndDate
from Overrides p1
left join Overrides p2 on p1.StartDate = DATEADD(day,1,p2.EndDate)
where p2.StartDate is null
union all
select p1.StartDate,p2.EndDate
from PrepareOverridePeriods p1
inner join Overrides p2 on p1.EndDate = DATEADD(day,-1,p2.StartDate)
),OverridePeriods as (
select ROW_NUMBER() over (order by StartDate) [RowNum],StartDate,MAX(EndDate) as EndDate
from PrepareOverridePeriods group by StartDate
)
,AllDates as (
select ROW_NUMBER() over (order by [Date]) [RowNum],data.Date from (
select dateadd(day,-1,OverridePeriods.StartDate) [Date] from OverridePeriods union all
select dateadd(day,+1,OverridePeriods.EndDate) [Date] from OverridePeriods union all
select StartDate [Date] from [System] union all
select EndDate [Date] from [System] ) as data
)
,NewPeriods as (
select sy.RowNum, s.[Date] [StartDate],n.[Date] [EndDate] ,sy.Value
from AllDates s
left outer join AllDates n on n.RowNum=s.RowNum+1
left outer join OverridePeriods o on s.[Date] between o.StartDate and o.EndDate and n.[Date] between o.StartDate and o.EndDate
left outer join [System] sy on s.[Date] between sy.StartDate and sy.EndDate
where
s.RowNum % 2 =1 and o.StartDate is null--group it by 2 and remove overriden areas
)
,Merged2 as (
select [RowNum], StartDate,EndDate,Value,'System' [RecordType] from NewPeriods
union all
select [RowNum], StartDate,EndDate,Value,'Override' [RecordType] from Overrides
)
select * from Merged2 order by StartDate
Я уверен, что может быть другой способ достижения результата, требуемого с помощью некоторого рекурсивного подхода, но сейчас это работает для меня.
На последнем шаге мы можем попытаться объединить результаты, если значение совпадает, но я не думаю, что это было запрошено.