Пересечение диапазонов дат

andMegaM
Дата: 06.06.2011 22:37:14
Доброго времени суток

Имеется две таблички
tab1
НачалоОкончание
01.01.201131.01.2011


tab2
НачалоОкончание
15.01.201114.02.2011


В этих табличках хранятся интервалы времени. Как из двух таблиц получить третью?:
НачалоОкончание
01.01.2011 14.01.2011
15.01.2011 31.01.2011
01.02.2011 14.02.2011


Т.е. если два диапазона пересекаются, то создать таблицу с тремя диапазонами:
Диапазоны дат
не пересекающийся диапазон tab1
пересечение диапазонов tab1 и tab2
не пересекающийся диапазон tab2


в каждой из таблиц tab1 и tab2 может содержаться по нескольку записей.
Ivan Durak
Дата: 07.06.2011 00:41:39
проще всего через юнион....
andMegaM
Дата: 07.06.2011 07:55:52
Что-то вообще не представляю как в данной ситуации поможет юнион.
Можно по-подробнее?
Владимир СА
Дата: 07.06.2011 08:44:49
andMegaM,

Что-то в этом роде:
declare @tab1 table (d_beg date, d_end date)
insert into @tab1 (d_beg, d_end) values ('20110101','20110131')
select * from @tab1

declare @tab2 table (d_beg date, d_end date)
insert into @tab2 (d_beg, d_end) values ('20110115','20110214')
select * from @tab2

select distinct t.d_date from
(
	select d_beg as d_date from @tab1
	union all
	select d_end as d_date from @tab1
	union all
	select d_beg as d_date from @tab2
	union all
	select d_end as d_date from @tab2
) t
order by t.d_date;


declare @tab3 table (d_beg date, d_end date)

declare @d_date date, @d_beg date, @d_end date;

declare cur_date cursor local for
select distinct t.d_date from
(
	select d_beg as d_date from @tab1
	union all
	select d_end as d_date from @tab1
	union all
	select d_beg as d_date from @tab2
	union all
	select d_end as d_date from @tab2
) t
order by t.d_date;

open cur_date;
fetch next from cur_date into @d_date;

while @@fetch_status = 0
begin
	set @d_beg = @d_date;
	fetch next from cur_date into @d_date;
	set @d_end = DATEADD(day, -1, @d_date);
	insert into @tab3 (d_beg, d_end) values (@d_beg,@d_end);
end;
close cur_date;
deallocate cur_date;

delete from @tab3 where d_beg = @d_date;
update @tab3 set d_end = @d_date where d_end = DATEADD(day, -1, @d_date);

select * from @tab3 order by d_beg
Ivan Durak
Дата: 07.06.2011 09:58:12
можно и без курсора
andMegaM
Дата: 07.06.2011 10:48:47
2 Ivan Durak

А можно вариант без курсора?
mike909
Дата: 07.06.2011 12:06:13
andMegaM
2 Ivan Durak

А можно вариант без курсора?


Решение в лоб с всеобщим объединением диапазонов и заполнением дырок:
declare @t1 table ([START] date, [END] date)
insert into @t1 ([START], [END]) values 
('20110110','20110120'),
('20110115','20110125'),
('20110117','20110220'),
('20110301','20110315')

declare @t2 table ([START] date, [END] date) 
insert into @t2 ([START], [END]) values 
('20110101','20110130'),
('20110310','20110320')

;with tb1( RN, [START], [END] ) as (
  SELECT ROW_NUMBER() OVER(ORDER BY [START] ) as RN, [START], [END]
  FROM @t1
)
,ts1( RN, [START], [END] ) as (
  SELECT ROW_NUMBER() OVER(ORDER BY [START]), t.[START], t.[END]
  FROM (
    SELECT MIN(t1.[START]) as [START], ISNULL(t2.[END], t1.[END]) as [END]
    FROM tb1 as t1
    OUTER APPLY(
      SELECT MAX( t2.[END] ) as [END]
      FROM tb1 as t2
      WHERE t1.RN < t2.RN
      and
        /*
            not	((t1.[END] < t2.[START]) or (t2.[END] < t1.[START]))
          and 
            not ((t1.[END] < t2.[START]) or (t2.[END] < t1.[START]))
        */
        --  После смены базиса 'OR' на 'AND'
          ((t2.[START] <= t1.[END]) AND (t1.[START] <= t2.[END]))
		  and
          ((t2.[START] <= t1.[END]) AND (t1.[START] <= t2.[END]))
      ) as t2
    GROUP BY ISNULL(t2.[END], t1.[END])
  ) as t
)
,tb2( RN, [START], [END] ) as (
  SELECT ROW_NUMBER() OVER(ORDER BY [START] ) as RN, [START], [END]
  FROM @t2
)
,ts2( RN, [START], [END] ) as (
  SELECT ROW_NUMBER() OVER(ORDER BY [START]), t.[START], t.[END]
  FROM (
    SELECT MIN(t1.[START]) as [START], ISNULL(t2.[END], t1.[END]) as [END]
    FROM tb2 as t1
    OUTER APPLY(
      SELECT MAX( t2.[END] ) as [END]
      FROM tb2 as t2
      WHERE t1.RN < t2.RN
        and
            ((t2.[START] <= t1.[END]) AND (t1.[START] <= t2.[END]))
        and
            ((t2.[START] <= t1.[END]) AND (t1.[START] <= t2.[END]))
    ) as t2
    GROUP BY ISNULL(t2.[END], t1.[END])
  ) as t
)
,ts( RN, [START], [END] ) as (
  SELECT ROW_NUMBER() OVER(ORDER BY [START]), t.[START], t.[END]
  FROM (
    SELECT [START], [END]
    FROM ts1
		
    UNION ALL 
		
    SELECT [START], [END]
    FROM ts2
  ) as t
)
,tss( RN, [START], [END] ) as (
  SELECT ROW_NUMBER() OVER(ORDER BY [START]), t.[START], t.[END]
  FROM (
    SELECT MIN(t1.[START]) as [START], ISNULL(t2.[END], t1.[END]) as [END]
    FROM ts as t1
    OUTER APPLY(
      SELECT MAX( t2.[END] ) as [END]
      FROM ts as t2
      WHERE t1.RN < t2.RN
        and
            ((t2.[START] <= t1.[END]) AND (t1.[START] <= t2.[END]))
        and 
            ((t2.[START] <= t1.[END]) AND (t1.[START] <= t2.[END]))
    ) as t2
    GROUP BY ISNULL(t2.[END], t1.[END])
  ) as t
)

select [START], [END], 1 as [isPresent]
from tss
union all
select t1.[END], t2.[START], 0 as [isPresent]
from tss as t1
inner join tss as t2 on
  t1.RN = t2.RN-1
order by [START]
Может это и не то, что Вы хотели, но мне было интересно решить задачу в такой постановке.
aleks2
Дата: 07.06.2011 12:49:48
andMegaM
Доброго времени суток

Диапазоны дат
не пересекающийся диапазон tab1
пересечение диапазонов tab1 и tab2
не пересекающийся диапазон tab2


в каждой из таблиц tab1 и tab2 может содержаться по нескольку записей.


Сколько диапазонов может ОДНОВРЕМЕННО участвовать в пересечении?
Добрый Э - Эх
Дата: 07.06.2011 12:57:20
Делать в лоб - рекурсивным СТЕ генерировать полный список дней заданных в таблицах интервалов, после чего соединять полученные полные наборы дат и группировать.
В этом случае можно будет подсчитать кол-во пересекающихся интервалов, выбрать не пересекающиеся даты и сделать всё что душе угодно.
aleks2
Дата: 07.06.2011 13:04:35
Добрый Э - Эх
Делать в лоб - рекурсивным СТЕ генерировать полный список дней заданных в таблицах интервалов, после чего соединять полученные полные наборы дат и группировать.
В этом случае можно будет подсчитать кол-во пересекающихся интервалов, выбрать не пересекающиеся даты и сделать всё что душе угодно.


А главное, успеть попить чайку и выспаться.