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 

Am I по праву отслеживать попытку рекурсивного запроса с помощью CTE? У кого-нибудь есть SQL, который может делиться?

+0

Что не хватает в моем более коротком ответе? –

ответ

1

Следующие ручки, инвертирующие диапазоны дат, и правильно обрабатывают диапазоны, которые перекрывают начало и/или конец целевого диапазона. EDIT: Обновлен для обработки случая, когда не найдено подходящих диапазонов.

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 WHEN cal.RN NULL THEN @PeriodEDate ELSE DateAdd (день, -1, cal.start_date) END, 'Ведущий' \t ОТ (SELECT @PeriodSDate AS дата_начала, @PeriodEDate А.С. датой_окончания) КАК защиту \t \t LEFT OUTER JOIN SortedDateRanges кал ВКЛ EXISTS (SELECT * FROM SortedDateRanges) \t ГДЕ \t \t (RN = 1 И cal.start_date > @PeriodSDate) \t \t ИЛИ \t \t (cal.RN IS NULL) ' –

+0

@ G.Stoynev - ответ был обн чтобы обрабатывать как пустую таблицу ввода, так и таблицу без диапазонов дат в целевом диапазоне. Казалось, проще всего добавить еще один «СОЮЗ», который выбирает из ниоткуда. – 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)