2013-06-02 23 views
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?

+0

我的簡短答案中缺少的是什麼? –

回答

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; 
+0

與我要去的地方非常相似。感謝您節省我的時間! –

+0

如果源集合爲空,您是否可以更新您的答案(爲了完整性)以包含邊緣案例。我認爲這樣做:' - ...加上任何前導日期範圍... \t SELECT @PeriodSDate,CASE當cal.RN爲NULL時@PeriodEDate ELSE DateAdd(day,-1,cal.start_date)END, '龍頭' \t FROM(SELECT @PeriodSDate AS起始日期,@PeriodEDate AS END_DATE)AS DEF \t \t LEFT OUTER JOIN SortedDateRanges CAL上存在(SELECT * FROM SortedDateRanges) \t WHERE \t \t(RN = 1 AND cal.start_date > @PeriodSDate) \t \t OR \t \t(cal.RN IS NULL)' –

+0

@ G.Stoynev - 答案已更新用於處理空的輸入表格或目標範圍內沒有日期範圍的表格。看起來最簡單的就是添加另一個無處不在的UNION。 – HABO

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)