1

我正在研究某种形式的内联(即不是 UDF os SPROC)选择,它将返回的日期范围转换为“负”日期范围的形式。这是“源”集:

SELECT id, SDate, EDate
FROM table
ORDER BY SDate

样本集:

1  01/05/2013  01/12/2013
2  01/13/2013  01/18/2013
3  02/05/2013  03/12/2013
4  07/08/2013  08/31/2013

搜索结果(假设我们只对 2013 年感兴趣)

01/01/2013 01/04/2013
01/19/2013 02/04/2013
03/13/2013 07/07/2013
09/01/2013 12/31/2013

我是否在正确的轨道上尝试使用 CTE 制作递归查询?有人有可以分享的SQL吗?

4

2 回答 2

2
declare @t table (SDate date, Edate date)
insert @t values ('2013-01-01', '2013-12-20')

declare @year int = 2013

;with dates as
(
select dateadd(year, @year - 1900, 0) d
union all
select d + 1
from dates where
d < dateadd(year, @year - 1899, -1)
),
b as
(
select d, grp = d-row_number() over (order by d) from dates 
where not exists (select 1 from @t where dates.d between SDate and EDate)
)
select min(d), max(d) from b group by grp
option (maxrecursion 366)
于 2013-06-02T16:34:48.150 回答
1

以下处理反转日期范围并正确处理与目标范围的开始和/或结束重叠的范围。 编辑:更新以处理未找到适用范围的情况。

declare @DateRanges as Table ( Id Int Identity, SDate Date, EDate Date );
insert into @DateRanges ( SDate, EDate ) values
  ( '20130105', '20130112' ),
  ( '20130113', '20130118' ),
  ( '20130205', '20130312' ),
  ( '20130708', '20130831' ),
  -- Additional test data for ranges that overlap the target date range, or not.  
  ( '20100101', '20130101' ),
  ( '20131127', '20140102' ),
  ( '19990208', '20041118' ),
  ( '20601113', '20990101' );
select * from @DateRanges order by SDate;

declare @StartDate as Date = '20130101';
declare @EndDate as Date = DateAdd( day, -1, DateAdd( year, 1, @StartDate ) );
select @StartDate as [@StartDate], @EndDate as [@EndDate];

with SortedDateRanges as (
  -- Sort the date ranges so that we have a dense row number for later use.
  select SDate, EDate, Row_Number() over ( order by SDate ) as RN
    from @DateRanges
    where EDate >= @StartDate and SDate <= @EndDate ),
  PairedDateRanges as (
  -- Pair the adjacent date ranges and turn them inside out.
  select DateAdd( day, 1, L.EDate ) as SDate, DateAdd( day, -1, R.SDate ) as EDate
    from SortedDateRanges as L inner join
      SortedDateRanges as R on R.RN = L.RN + 1 )
  -- The result is all of inside out date ranges that are valid, i.e. don't end before they start...
  select SDate, EDate, 'Fill Gap' as Reason
    from PairedDateRanges
    where SDate <= EDate
  union
  --  ... plus any leading date range ...
  select @StartDate, DateAdd( day, -1, SDate ), 'Leading'
    from SortedDateRanges
    where RN = 1 and SDate > @StartDate
  union  
  --  ... plus any trailing date range ...
  select DateAdd( day, 1, EDate ), @EndDate, 'Trailing'
    from SortedDateRanges
    where RN = ( select Max( RN ) from SortedDateRanges ) and EDate < @EndDate
  union
  -- ... or we have nothing.
  select @StartDate, @EndDate, 'No Data'
    where not exists ( select 42 from SortedDateRanges )
  order by SDate;
于 2013-06-02T17:33:55.840 回答