使用SQL查找一组间隔的数学补充

时间:2017-08-31 13:34:20

标签: sql-server tsql date intervals

我正在思考需要通过SQL解决的以下问题。设有自然数的区间[a,b]和[a,b]的所有子集的(有限的)区间A组。我们想确定补充auf A,即一组间隔B,使得A + B = [a,b],A和B成对不相交。

例如:给定[a,b] = 2017年的日期("所有日期"),以及3月 - 6月,4月,4月 - 7月和11月的间隔("可能的天数& #34)。现在产生间隔jan-feb,aug-oct和dec("不可能的日子")。所有间隔都是相应的。应该通过开始日期和结束日期来定义。

我尝试了以下内容。制作2017年的日历,并检查每天是否包含在两个间隔中。从这些日子开始,构建相应的间隔。到目前为止,它似乎很复杂,我开始认为这种解决方案在SQL方面有点不合时宜。但也许它只是我的实施。你怎么看?你想知道更好的方法吗?

来自法兰克福的问候,

约翰

1 个答案:

答案 0 :(得分:0)

只要您使用已知的固定范围,您就可以轻松找到候选者作为任何当前间隔之外的开始或结束。然后根据日期顺序将它们配对:

declare @RangeStart date
declare @RangeEnd date
select @RangeStart = '20170101',@RangeEnd = '20171231'

declare @intervals table (
    StartAt date not null,
    EndAt date not null
)
insert into @intervals (StartAt,EndAt) values
('20170301','20170630'),
('20170401','20170430'),
('20170401','20170731'),
('20171101','20171130')

;With Starts as (
    select
        @RangeStart as StartDT
    where
        not exists (select * from @intervals i where @RangeStart between i.StartAt and i.EndAt) --Start outside an interval
    union all
    select
        DATEADD(day,1,i1.EndAt)
    from
        @intervals i1
            left join
        @intervals i2
            on
                DATEADD(day,1,i1.EndAt) between i2.StartAt and i2.EndAt --No succeeding interval
    where
        i2.EndAt is null
), Ends as (
    select
        @RangeEnd as EndDT
    where
        not exists (select * from @intervals i where @RangeEnd between i.StartAt and i.EndAt) --End outside an interval
    union all
    select
        DATEADD(day,-1,i1.StartAt)
    from
        @intervals i1
            left join
        @intervals i2
            on
                DATEADD(day,-1,i1.StartAt) between i2.StartAt and i2.EndAt --No preceding interval
    where
        i2.StartAt is null
), OrderedStarts as (
    select StartDT,ROW_NUMBER() OVER (ORDER BY StartDT) as rn
    from Starts where StartDT between @RangeStart and @RangeEnd
), OrderedEnds as (
    select EndDT,ROW_NUMBER() OVER (ORDER BY EndDt) as rn
    from Ends where EndDT between @RangeStart and @RangeEnd
)

select
    os.StartDT,oe.EndDT
from
    OrderedStarts os
        inner join
    OrderedEnds oe
        on
            os.rn = oe.rn

结果:

StartDT    EndDT
---------- ----------
2017-01-01 2017-02-28
2017-08-01 2017-10-31
2017-12-01 2017-12-31

即 - 有效的开始日期是我们的范围的开始或任何其他间隔之后的第二天,前提是它不与另一个间隔重叠。同样为有效目的。